Compare commits

276 Commits

Author SHA1 Message Date
8db72037e0 refactor(form): vbproj aktualisieren, um mit .net 8 zu konfigurieren.
- Verwandte Referenzen für net 9 hinzufügen.
 - Backup-XML-Datei für altes vbproj erstellen.
2025-09-10 13:27:48 +02:00
6b89b9bbf2 refactor(ModifyDocStatusCommandBase): add EnvelopeId and ReceiverId properties 2025-09-09 23:15:19 +02:00
83ff3da795 fix(ReceiverAlreadySignedQuery) 2025-09-09 21:57:59 +02:00
5bb3341f43 refactor(JsonSerializerSettings): set Ignore ReferenceLoopHandling 2025-09-09 20:39:21 +02:00
7568274c77 refactor(ReceiverSecretDto): remove 2025-09-09 20:32:35 +02:00
c59b179a8f merge with EnvelopeReceiverBasicDto 2025-09-09 20:05:34 +02:00
248c5bce5a refactor(Common.Model): rename as Common.Query 2025-09-09 19:39:50 +02:00
c76a772768 fix references 2025-09-09 19:23:04 +02:00
3ba7bfd15a refactor(EnvelopeHistoryDto): rename HostoryDto 2025-09-09 19:01:20 +02:00
e8f2c868b1 refactor(EnvelopeHistory): rename as History 2025-09-09 18:58:58 +02:00
ae669d05e7 refactor(EnvelopeDocument): rename as Document 2025-09-09 18:56:55 +02:00
fbbc05814f refactor(DocumentReceiverElementDto): rename as SignatureDto 2025-09-09 18:56:18 +02:00
ec57906290 refactor(EnvelopeDocumentDto): rename as DocumentDto 2025-09-09 18:52:58 +02:00
fbfc20705d refactor(Extensions): move to common 2025-09-09 18:26:06 +02:00
895eb8977e refactor(Notifications): move to Common 2025-09-09 18:24:42 +02:00
428f45bff1 refactor(Model): move to Common 2025-09-09 18:22:31 +02:00
9339f24bf1 refacor(EnvelopeFlag): move to Common 2025-09-09 18:21:24 +02:00
390cbf9db5 refactor(Dto): move to common 2025-09-09 18:18:47 +02:00
570a192438 refactor(Configurations): move to common 2025-09-09 18:17:39 +02:00
c8a0264ed8 refactor(SQL): move to common 2025-09-09 18:17:00 +02:00
207bc8bcbd convert csrfToken to get method 2025-09-09 18:11:14 +02:00
1ac2a476d2 refactor(DocumentReceiverElementDto): remove Top and Left-properties 2025-09-09 16:55:34 +02:00
Developer 02
41f5d9f1e4 refactor: make CreateShowEnvelopeView and SignInEnvelopeAsync generic method 2025-09-09 14:08:20 +02:00
2ae6dda27e refactor(EnvelopeMailService): remove SendTFAQrCodeAsync method 2025-09-08 17:41:34 +02:00
e5a25c5893 refactor(EnvelopeReceiverDto): add option to make Receiver property generic 2025-09-08 17:32:00 +02:00
7d0648ede4 Um den Receiver in ReceiverSecretDto umwandeln zu können, fügen Sie ein Mapping-Profil hinzu. 2025-09-08 17:25:40 +02:00
bb6ca82289 feat(ReceiverSecretDto): ReceiverDto wurde so hinzugefügt, dass es vererbt wird.
- Die Eigenschaft TotpSecretkey wurde aus ReceiverDto entfernt.
 - Die Eigenschaft TotpSecretkey wurde zu ReceiverSecretDto hinzugefügt.
2025-09-08 17:23:20 +02:00
ed7068fe71 refactor(ReceiverReadDto): rename as ReceiverDto 2025-09-08 17:19:38 +02:00
876c5def56 fix(Extensions): update to add suffix 2025-09-08 16:53:44 +02:00
40697435ff fix(EnvelopeDto): convert type of Status-property to EnvelopeStatus 2025-09-08 16:20:16 +02:00
b43399ad01 fix(EGDbContext): add Envelope-property to Envelope-History relation definition on model builder 2025-09-08 16:16:22 +02:00
f41f26b810 refactor(DocumentReceiverElement): make AddedWhen nullable and add ChangedWho-property 2025-09-08 15:00:02 +02:00
442b7f7451 refactor(EGDbContext): split to regions 2025-09-08 13:03:12 +02:00
beada59593 refactor(HomeController): update EnvelopeLocked to use _mediator 2025-09-08 12:58:29 +02:00
b78aff102a remove then-calback method implementation on EnvelopeLocked-endpoint. 2025-09-08 11:55:08 +02:00
67f068ef38 refactor(Envelope): rename Receivers property as EnvelopeReceivers 2025-09-08 11:48:54 +02:00
122df4bd62 chore: remove DevExpress.Data.v21.2 and GdPicture.NET.14 2025-09-08 11:32:53 +02:00
741ebc8975 minify js 2025-09-05 15:01:29 +02:00
ada76d5030 refactor(HomeController): add SetLanguage and GetLanguages endpoints 2025-09-05 14:42:04 +02:00
845d06fc4c refactor(HomeController): add HttpGet-attribute to Error404 (fallback) endpoint 2025-09-05 14:32:22 +02:00
59105caffc fix(pdf-burner): Korrektur des Versatzes von Siegelbildern für Tintenunterschriften
- Hinzufügen des Parameters „yOffset“ zu „AddImageAnnotation“, um die Position der Siegel anzupassen.
- Aktualisierung von „AddInstantJSONAnnotationToPDF“, um den Versatz für das erste Unterschriftssiegel anzuwenden.
- Stellt sicher, dass Tintenunterschriftssiegel in PDF-Dateien korrekt dargestellt werden, ohne andere Anmerkungen zu beeinträchtigen.
2025-09-05 13:25:25 +02:00
fa8d2f5f62 feat(pdfburner): Berechnung für Anmerkungsindex innerhalb der Signaturgruppe hinzufügen 2025-09-05 11:26:09 +02:00
dbf42e13d9 feat(pdfburner): Berechnung des Signaturindexes bei der Anmerkungsverarbeitung hinzugefügt
- Variablen `annotCountPerSign`, `annotIndex` und `calcSignIndex` eingeführt
- Berechnung von `signIndex` während der Anmerkungsiteration hinzugefügt
- Bestehende Logik für Bild-, Tinten- und Widget-Anmerkungen beibehalten
2025-09-05 11:01:36 +02:00
e48a86e21c feat: add EnvelopeKeyRedirController to redirect /EnvelopeKey/{*path} to /Envelope/{*path} 2025-09-04 23:44:01 +02:00
1e6c9ed40e refactor(HomeController): ranme EnvelopeKey route as Envelope 2025-09-04 23:38:11 +02:00
1d605e9da3 fix(EnvelopeController): remove envelopeKey route parameter 2025-09-04 23:17:24 +02:00
77070a8cfc convert csrfToken constan variable 2025-09-04 23:07:32 +02:00
c479ea4179 remove README.md from static files 2025-09-04 19:10:31 +02:00
369d101d7b refactor(network): Netzwerkklasse entfernen und Methoden statisch machen.
- Unnötige Funktionen entfernen.
2025-09-04 19:09:21 +02:00
86eb687296 remove npm components of bootstrap-icons 2025-09-04 19:01:41 +02:00
aa8f46a303 remove old envelope srrvice dependencies 2025-09-04 18:56:02 +02:00
2c825d2fe3 update post Envelope to handle without object 2025-09-04 18:45:00 +02:00
1e1517f88a refactor(EnvelopeController): remove EnvelopeOldService 2025-09-04 18:01:01 +02:00
124523ad88 configure attributes 2025-09-04 17:46:16 +02:00
9c48b230b4 convert PlaceHolders-property to CreatePlaceHolders callback-method 2025-09-04 17:01:40 +02:00
95fe1aefcf merge placeholders dictionary 2025-09-04 16:45:56 +02:00
1b7a42fd7e remove cancelation token from ConfigEmailOut 2025-09-04 16:41:26 +02:00
73da768ed3 update ConfigEmailOut to add notifications 2025-09-04 09:28:13 +02:00
f20243d02c update to configure email out 2025-09-03 18:39:33 +02:00
c1c30caeec init EmailOut 2025-09-03 17:55:50 +02:00
5ccd1fee26 init SendMailHandler to read tempalte and replace placeholders 2025-09-03 17:46:24 +02:00
954d665ac3 remove unnecessary references 2025-09-03 16:58:07 +02:00
bb85437cc4 arrange folder structure of notification 2025-09-03 16:57:25 +02:00
763f022a5e add history handler 2025-09-03 16:42:32 +02:00
f87f8a1d17 refactor(DocSignedNotificationStatusHandler): rename as DocSignedNotificationAnnotationHandler 2025-09-03 16:31:14 +02:00
fb5d2110bd add DocSignedNotificationStatusHandler and implement the natification into EnveloepControler 2025-09-03 16:24:51 +02:00
aafed0f4f4 init DocSignedNotificationStatusHandler 2025-09-03 15:47:37 +02:00
a433654f86 refactor(DocSignedNotification): Aktualisierung, um von EnvelopeReceiverDto zu erben.
- Erweiterungsmethoden für die Zuordnung hinzufügen.
2025-09-03 15:38:41 +02:00
b599ada864 add DocSignedNotification 2025-09-03 15:17:03 +02:00
6b00ab6a45 refactor(SaveDocStatusCommand): update to return DocumentStatusDto. 2025-09-03 13:03:09 +02:00
6ab85f25eb refactor(SignDocBehavior): add to handle the events after document status updateted 2025-09-03 12:49:16 +02:00
fa46dd1fa8 feat(Format): create to centrilize the formats.
- Add json serializer settings for diagnostics and implement to DocumentController
2025-09-03 11:06:47 +02:00
e623680c3f refactor(DocumentController): migrate from legacy service to MediatR pattern
- Replace obsolete DocumentController implementation using EnvelopeOldService and direct dependencies
- Migrate to MediatR-based query handling via ReadEnvelopeReceiverAsync
- Remove obsolete constructors and actions marked for deprecation
- Add proper error handling with NotFoundException for missing envelope or document data
- Improve logging with structured JSON output for diagnostics
- Update base class from BaseController to ControllerBase and add ApiController attribute
- Rename action to GetDocument and apply explicit HttpGet routing
- Use IMediator for clean separation of concerns and alignment with CQRS pattern
2025-09-03 10:51:34 +02:00
f6e34c6d91 refactor(HistoryTests): Verbesserung von HistoryTests mit realistischen Daten und konsistenter Struktur
- Verwendung tatsächlicher Umschlag- und Empfängerdaten anstelle von magischen Zahlen
- Ersetzen von fest codierten EnvelopeId und UserReference durch dynamisch erstellte Entitäten
- Aktualisierung von ReadHistoryQuery zur Verwendung der Objektinitialisierungssyntax
- Fügen Sie CancellationToken zu Mediator.Send-Aufrufen hinzu, um Konsistenz zu gewährleisten
- Verwenden Sie EnvelopeHistoryDto explizit in der Assertion
- Verbessern Sie die Lesbarkeit und den Realismus des Tests, indem Sie vollständige Voraussetzungdaten einrichten
- Korrigieren Sie die Schreibweise im Kommentar: „EnvelopeReceiver” -> „envelope receiver”
2025-09-03 10:08:42 +02:00
fc443fb87f refactor(ReadHistoryQuery): Aktualisierung zur Verwendung von getter-initter. 2025-09-03 09:34:12 +02:00
1c9d0a6c47 refactor(MappingProfile): update to ignore Envelope, Sender and Receiver 2025-09-02 23:58:35 +02:00
23ec4fe322 remove re-read process 2025-09-02 23:14:16 +02:00
8ca0519dbc feat(history): CreateHistoryCommand wurde verbessert, um DTO zurückzugeben und den Empfänger zu validieren.
- Der Rückgabetyp des Handlers wurde von „long?“ zu „EnvelopeHistoryDto?“ geändert.
 - AutoMapper-Integration für die Zuordnung von EnvelopeHistory zu DTO hinzugefügt.
 - IRepository<EnvelopeReceiver> zur Validierung der Benutzerreferenz eingeführt.
 - Validierung implementiert, um sicherzustellen, dass genau ein Empfänger gefunden wird.
 - BadRequestException-Behandlung für fehlende oder mehrere Empfänger hinzugefügt.
2025-09-02 23:11:41 +02:00
c67bac3e16 test(history): Aktualisieren Sie HistoryTests, um die Erstellung von Umschlägen auf Repository-Basis zu verwenden. 2025-09-02 18:48:18 +02:00
6cdd1db7a9 feat(Fake): add extension method to Fake to create Envelope 2025-09-02 15:25:59 +02:00
a87a524271 feat(MappingProfile): UUID-Generierung beim Zuordnen von CreateEnvelopeCommand zu Envelope hinzufügen 2025-09-02 12:19:06 +02:00
ddb2439b29 add mapping profile to convert CreateEnvelopeCommand to Envelope 2025-09-01 17:30:38 +02:00
d48514bbad refactor(Extensions): update CreateEnvelopeCommand to make UseSQLExecutor false 2025-09-01 17:27:14 +02:00
00077a647a feat(envelopes): add support for SQLExecutor or repository when creating envelopes
- Refactored `CreateEnvelopeCommandHandler` to resolve dependencies via `IServiceProvider`
- Added `IRepository<Envelope>` to allow repository-based envelope creation
- Updated handler logic to choose between `IEnvelopeExecutor` and repository based on `UseSQLExecutor` flag
2025-09-01 17:23:40 +02:00
ee7eb08e75 refactor(CreateEnvelopeCommand): add UseSQLExecutor-property 2025-09-01 17:07:16 +02:00
6a34b65825 rename as ToEnvelopeKey 2025-09-01 15:43:13 +02:00
20d312a84e update to create key by EncodeEnvelopeReceiverId 2025-09-01 15:42:33 +02:00
87c5e7e4de feat(HistoryTests): add create receiver logic 2025-09-01 15:27:32 +02:00
bb93b980b4 refactor(HistoryTests): create and implement TestBase 2025-09-01 15:19:52 +02:00
950ae5a418 Update HistoryTests with new commands and setup changes
- Added new using directives for additional namespaces.
- Replaced `_host.AddSampleReceivers()` with `_host.AddSamples()` in the Setup method.
- Introduced `createEnvelopeCmd` in the test method to create an envelope before executing the history command.
2025-09-01 15:06:10 +02:00
582cc1eb13 Refactor CreateHistoryCommand and update tests
- Simplified `CreateHistoryCommand` method by removing generic type parameters.
- Updated `using` directives in `HistoryTests` to include necessary constants.
- Revised test method to utilize `Fake.Provider.CreateHistoryCommand` for improved maintainability.
2025-09-01 14:52:49 +02:00
9434f83b3e Refactor EnvelopeReceiver and History controllers
- Added using directive for EnvelopeGenerator.Domain.Constants.
- Updated ignore_statuses type in EnvelopeReceiverController.
- Improved resource management with using statements for SqlConnection and SqlCommand.
- Changed from Array.Empty<Signature>() to Enumerable.Empty<Signature>() for better performance.
- Simplified GetEnvelopeStatus method in HistoryController by using Status directly.
2025-09-01 14:21:53 +02:00
bbe93dad45 Refactor Receiver handling to use ReceiverVM
Updated ReceiverModel and EnvelopeEditorController to utilize ReceiverVM instead of Receiver in multiple function signatures. This change enhances the structure and flexibility of receiver data handling. Adjusted related methods in frmEnvelopeEditor to align with the new view model approach.
2025-09-01 13:53:09 +02:00
0a876fe486 Refactor EnvelopeEditorController for type consistency
- Updated constructor to cast receivers to EnvelopeReceiver.
- Changed ResendReceiverInvitation parameter from Receiver to ReceiverVM.
- Modified ValidateEnvelopeForSending to use EnvelopeReceiver and correct ID for validation.
2025-09-01 13:47:32 +02:00
a584a548d6 Refactor InsertReceiver method in EnvelopeEditorController
Removed conditional check for receiver ID in InsertReceiver.
The method now always attempts to insert the receiver,
simplifying the logic and ensuring consistent behavior.
2025-09-01 13:27:38 +02:00
3e3c9d4c54 Refactor Receiver to ReceiverVM in EnvelopeEditor and Main
Updated the `oReceiver` variable type from `Receiver` to `ReceiverVM` in the `frmEnvelopeEditor` and `frmMain` classes to align with a view model pattern. Modified the `Controller.DeleteReceiver` method to use `oReceiver.Receiver`, reflecting the new structure. Additionally, created `BindingList` of `ReceiverVM` to support a more MVVM architecture.
2025-09-01 13:00:03 +02:00
33fa4b76f5 Refactor receiver handling and update solution structure
- Updated `BaseController.vb` to use `Receiver.Id` for deletion.
- Changed `Receivers` property in `frmEnvelopeEditor.vb` to `BindingList(Of ReceiverVM)` and adjusted initialization and usage throughout the file.
- Modified `frmFieldEditor.vb` to use `List(Of ReceiverVM)` for receivers and updated related methods.
- Adjusted color assignment logic in `frmMain.vb` to utilize a new `Value` class.
- Added new project for `EnvelopeGenerator.Form` in the solution file.
- Introduced new enums `ContractType` and `PageOrientation` in their respective files.
2025-09-01 12:55:15 +02:00
7a84726a3b Refactor constant imports and enhance safety checks
Updated constant imports from static to regular usage across multiple controllers.
Improved null safety in `DebugEnvelopes.cshtml` by ensuring the `Receivers` property is not null before access.
Modified authorization attribute in `DocumentController.cs` to directly reference `Domain.Constants.ReceiverRole.FullyAuth`.
2025-09-01 11:31:39 +02:00
27d97ed12a Add constants import and refactor PDF burner params
- Added import for `EnvelopeGenerator.Domain.Constants` in
  `Scheduler_Envelopetask_API.vb` and `Scheduler_FinishEnvelope.vb`
  to access necessary constants.

- Updated the key for `_pdfBurnerParams` in
  `Scheduler_FinishEnvelope.vb` from
  `Domain.Constants.PDF_BURNER_PARAMS` to
  `Value.PDF_BURNER_PARAMS` for better organization.
2025-09-01 11:24:23 +02:00
f699e5a9aa Refactor constant references across multiple files
Updated import statements and changed references from
`Domain.Constants` to `Value` in `APIEnvelopeJob.vb`,
`FinalizeDocumentJob.vb`, `HistoryModel.vb`, `EmailService.vb`,
and job scheduling files. This standardizes constant access
and improves code maintainability and readability.
2025-09-01 11:20:57 +02:00
dc723d9f02 Refactor envelope status handling and imports
Updated `EnvelopeStatus` type in place of `EnvelopeStatusQuery` across multiple files, enhancing the domain model. Added `using` directives for `EnvelopeGenerator.Domain.Constants` in several files, improving clarity and maintainability. Modified import statements in Visual Basic files and repository classes to standardize imports. Overall, these changes aim to refine the structure and clarity of the codebase.
2025-09-01 11:17:27 +02:00
48ce0d5f32 Refactor EnvelopeStatus usage and streamline code
- Added `EnvelopeGenerator.Domain.Constants` in multiple files, replacing `Domain.Constants.EnvelopeStatus` with `EnvelopeStatus` for improved readability.
- Modified `CreateEnvelopeCommandHandler.cs` to directly use `request.UserId`, eliminating unnecessary local variable assignment.
- Identified potential duplication in query logic within `ReceiverAlreadySignedQuery.cs`.
- Updated `IEnvelopeService`, `EnvelopeReceiverService`, and `EnvelopeService` to ensure consistent usage of the simplified `EnvelopeStatus`.
- Overall, these changes enhance code maintainability and clarity.
2025-09-01 11:11:30 +02:00
ef7e694c9f refactor(Application.Envelopes.Queries.EnvelopeStatus): rename as EnvelopeStatusQuery 2025-09-01 11:06:41 +02:00
c5c040fb15 Refactor enum usage in codebase
Updated references from `Constants` to direct usage of `EnvelopeStatus` and `EmailTemplateType` enums. This change enhances code readability and reduces dependency on the `Constants` class.

Modified properties and parameters across command classes, DTOs, repositories, and services to use the enums directly. Updated `ModifyDocStatusCommandBase`, `EnvelopeHistoryDto`, and `ResetEmailTemplateCommand` for improved clarity.

Consistent updates across multiple files indicate a systematic refactoring aimed at streamlining the codebase and improving maintainability. Comments and documentation have also been revised to reflect these changes.
2025-09-01 11:04:40 +02:00
fc4187bb9e Refactor constants and update CreateHistoryCommand
Updated `CreateHistoryCommand.cs` to use the `EnvelopeStatus` enum directly from the `EnvelopeGenerator.Domain.Constants` namespace. Refactored `Constants.cs` to consolidate multiple enums related to statuses, types, and roles. Modified `Fake.cs` to align with the new enum structure and introduced `Value.cs` for better organization of constant string values. Overall, these changes enhance code clarity and maintainability.
2025-09-01 10:41:36 +02:00
ae4f5560fe Remove unused enums from Constants.cs
This commit removes the `ContractType` and `PageOrientation` enums from the `EnvelopeGenerator.Domain.Constants.cs` file. The `ContractType` enum included values for `Contract` and `ReadAndSign`, while the `PageOrientation` enum included values for `Portrait` and `Landscape`. The `ElementType` and `ColorType` enums remain unchanged.
2025-09-01 10:19:36 +02:00
09eb91b6be Add history command support and enum selection method
- Introduced new using directives for histories and models.
- Added `PickEnum<T>` method to randomly select enum values.
- Renamed region from `#region Envelope Command` to `#region Envelope`.
- Implemented `CreateHistoryCommand<TEnvelopeQuery, TReceiverQuery>` for creating history commands.
- Organized new method under a new region `#region History`.
2025-09-01 10:16:50 +02:00
838d7e3ab8 refactor(ReceiverGetOrCreateCommand): update to use getter inittier 2025-08-29 16:33:01 +02:00
542d80c439 fix(CreateEnvelopeReceiverCommandHandler): remove request.UserId null-check 2025-08-29 16:23:44 +02:00
145a2ebe8f feat(Extensions): add PDF generation support for EnvelopeGenerator tests
- Introduced QuestPDF dependency to generate PDF documents in tests.
- Added `CreatePdfAsBase64` extension method for generating random PDF content.
- Added `CreateDocumentCommand` and `CreateDocumentCommands` helpers for creating document test data.
- Refactored using statements and added `EnvelopeGenerator.Application.EnvelopeReceivers.Commands`.
- Maintains existing sample user and receiver setup for integration testing.
2025-08-29 16:11:14 +02:00
9cf776fa98 fix(CreateEnvelopeReceiverCommand): update to use getter-setter 2025-08-29 15:33:03 +02:00
68878c0fc8 feat(Fake): Erweiterung von Fake.Host um Repository-Zugriff und Unterstützung für Beispielbenutzer
- IRepository<TEntity>-Verknüpfung zum Auflösen von Repositorys hinzugefügt
 - AddSamples()-Hilfsprogramm zum gemeinsamen Initialisieren von Empfängern und Benutzern eingeführt
 - SampleReceivers mit Validierungsüberwachung verbessert
 - Beispiel für Benutzerinitialisierung über Repository mit AddSampleUser() hinzugefügt
 - using-Anweisungen aktualisiert, um DigitalData.Core.Abstraction.Application.Repository und DigitalData.UserManager.Domain.Entities einzubeziehen
2025-08-29 14:57:51 +02:00
d3e5d3d791 refactor(Extensions): add CreateUserCommand and CreateUserCommands 2025-08-29 14:37:24 +02:00
a7f6b94d20 create CreateUserCommand with handler and mapping profile 2025-08-29 14:31:36 +02:00
777f20eddb create faker-extension method for Envelope Commands 2025-08-29 14:00:00 +02:00
c14ffceee4 refactor(CreateEnvelopeCommand): update to generate with getter-setter 2025-08-29 13:41:10 +02:00
e9202ad23e refactor(Fake): Ersetzen der Roh-E-Mail-Generierung durch CreateReceiverCommand-Helfer
- Direkte Verwendung von `Internet.EMailList()` für Beispielempfänger entfernt.
- Erweiterungsmethoden `CreateReceiverCommand` und `CreateReceiverCommands` in `Faker` eingeführt.
- `AddSampleReceivers` aktualisiert, um `CreateReceiverCommands` für eine sauberere, konsistentere Testdatenerstellung zu verwenden.
2025-08-29 13:25:36 +02:00
954eff7101 refactor(tests): replace static sample emails with Bogus-generated random emails 2025-08-29 13:12:33 +02:00
ac501dffb1 fix(Mock): rename Fake 2025-08-29 12:53:03 +02:00
baf2207d03 refactor(Mock): Host-Klasse für csetup erstellen und MediatR integrieren
- Migration zu Microsoft.Extensions.Hosting.CreateDefaultBuilder
- Optionale Parameter und echte DB-Unterstützung in Tests entfernt
- InMemoryDatabase zum Testen hinzugefügt
- MediatR für die Befehlsverarbeitung integriert
- Beispielmethode zum Initialisieren von Empfängern im Test-Host hinzugefügt
2025-08-29 12:46:38 +02:00
6863ada4be feat(HistoryTests): add Receiver to provide random receiver 2025-08-29 11:11:15 +02:00
8a22075abe update to use deconstructed 2025-08-29 11:05:52 +02:00
bcb2e79fa1 feat: Duplikatsprüfung beim Erstellen eines Empfängers hinzufügen
- `CreateReceiverCommand` wurde aktualisiert, sodass nun `(Id, AlreadyExists)` anstelle von nur `Id` zurückgegeben wird.
- Der Handler wurde geändert, um zu überprüfen, ob bereits ein Empfänger mit derselben E-Mail-Adresse vorhanden ist.
- Es wird nur dann ein neuer Empfänger erstellt, wenn dieser noch nicht vorhanden ist.
- `Microsoft.EntityFrameworkCore` wurde für die Abfrageunterstützung hinzugefügt.
2025-08-29 10:58:27 +02:00
c8dae1d8ff test: extend HistoryTests by adding receiver initialization with CreateReceiverCommand 2025-08-29 10:45:00 +02:00
cc2db8716e refactor(CreateReceiverCommand): add handler 2025-08-29 10:25:33 +02:00
b939e19334 move mapping profile 2025-08-29 10:07:45 +02:00
16e769d916 rename UpdateReceiverCommand 2025-08-29 10:04:25 +02:00
befbacad7c move ReceiverUpdateDto 2025-08-29 10:04:04 +02:00
aa1e218b37 remove lazy loading of signature 2025-08-29 10:03:19 +02:00
ab9a6cd595 rename CreateReceiverCommand 2025-08-29 10:02:01 +02:00
8783cb9cd8 move ReceiverCreateDto to commands 2025-08-29 10:01:17 +02:00
e49be2b7c3 make set up Setup 2025-08-29 09:57:57 +02:00
14a565d202 refactor(HistoryTests): Vereinfachung von HistoryTests durch direkte Einbindung von IMediator
- Entfernen der benutzerdefinierten Hilfsmethode Send<T>
 - Einführung der Mediator-Eigenschaft, die aus dem DI-Container aufgelöst wird
 - Ersetzen aller Send(request)-Aufrufe durch Mediator.Send(request)
 - Reduzierung unnötiger Indirektionen, wodurch Tests übersichtlicher und leichter lesbar werden
2025-08-29 09:52:34 +02:00
dc42a76f31 Merge branch 'master' of http://git.dd:3000/AppStd/EnvelopeGenerator 2025-08-29 09:39:08 +02:00
Developer 02
dee58bec4b fix EnvelopeReceiverController 2025-08-28 20:12:16 +02:00
Developer 02
5bd045b998 init HistoryTests 2025-08-28 20:07:34 +02:00
Developer 02
fbbacb30bc remove wrong principal key 2025-08-28 18:47:41 +02:00
Developer 02
0b879b2f5b refactor(Extension): move to Application.Extensions and remove the project.
- update references
2025-08-28 18:46:55 +02:00
Developer 02
a343312f97 refactor(HistoryController): update to use CancellationToken 2025-08-28 18:36:11 +02:00
Developer 02
dad43de8b1 refactor(ReadHistoryQuery): update to not throw exception.
- update controller to throw NotFound if the list is empty
2025-08-28 18:30:27 +02:00
Developer 02
f7c988be9b refactor(ReadHistoryQuery): mvoe HisotryQuery to Queries directory and remove Read dir 2025-08-28 18:20:18 +02:00
Developer 02
e0af5b769d refactor(ReadHistoryQuery): update to use dto and remove response class 2025-08-28 18:15:47 +02:00
Developer 02
b8c348afb6 refactor(ReadHistoryQueryHandler): update to use IRepository<EnvelopeHistory> 2025-08-28 17:52:24 +02:00
79dc4ba599 refactor: remove HasPrincipalKey-statement 2025-08-28 11:05:40 +02:00
3616d43f2e fix(EGDbContext): remove principal key mapping on DocumentStatus.Receiver 2025-08-27 15:24:26 +02:00
d5443b223c fix(EGDbContext); remove Uuid from principal key 2025-08-27 15:12:09 +02:00
c456cb0301 refactor(CreateHistoryCommand); update to use EnvelopeReceiverQueryBase and related extension methods 2025-08-27 14:41:20 +02:00
1c0c23aca4 remove url encoder injection 2025-08-27 11:42:33 +02:00
05d7ac7864 refactor(ReadEnvelopeReceiverQueryHandler): update to use extension method and move to related query class 2025-08-27 11:39:03 +02:00
95e793b081 update read envelope, receiver and envelope-receiver queries to inhered from query-bases 2025-08-27 11:04:30 +02:00
dfa1667939 refactor(ReadDocumentQuery): update to add CancellationToken 2025-08-27 10:50:51 +02:00
Developer 02
8a4d3ff6f9 refactor(SaveDocStatusCommand): simplify repository filtering in SaveDocStatusCommandHandler
- Replaced explicit envelope and receiver expression filters with a single `.Where(request)` call.
- Removed redundant checks for null and empty values in handler.
- Updated using statements to include `EnvelopeGenerator.Application.Extensions`.
- Maintains the same functionality while reducing code complexity.
2025-08-26 22:34:54 +02:00
Developer 02
783d91a658 feat(QueryExtensions): add combined EnvelopeReceiver query support in QueryExtensions 2025-08-26 22:32:34 +02:00
Developer 02
ad032b2bdf fix(QueryExtensions): update to use base classes instead of interfaces 2025-08-26 22:23:26 +02:00
Developer 02
f2876d8995 move model intterfaces to interfaces dir 2025-08-26 22:09:45 +02:00
Developer 02
5468d7b2aa feat: add QueryExtensions for filtering by Envelope and Receiver
- Introduced extension methods on IQueryable<TEntity> to filter entities
  by Envelope (Id or Uuid) and Receiver (Id, EmailAddress, or Signature).
- Throws BadRequestException if no valid identifier is provided when notnull = true.
- Improves query handling consistency across application layer.
2025-08-26 22:05:33 +02:00
Developer 02
b005c194d3 create and implement IHasEnvelopeQuery and IHasReceiverQuery 2025-08-26 19:43:55 +02:00
Developer 02
dee6608390 add and implement IHasEnvelope and IHasReceiver 2025-08-26 19:20:44 +02:00
Developer 02
8b53eae6da init QueryExtensions 2025-08-26 18:51:57 +02:00
Developer 02
405b619bdc refactor(ReceiverAlreadySignedQuery): update to use common models 2025-08-26 17:29:07 +02:00
Developer 02
c5918b8e49 refactor(CreateHistoryCommand): update to use EnvelopeQuery- and ReceiverQueryBase 2025-08-26 17:16:30 +02:00
Developer 02
05cd8a05f4 refactor(MappingProfile): create to handle model mappings 2025-08-26 17:13:21 +02:00
Developer 02
2355a566e4 refactor(EnvelopeReceiverQueryBase): simplify EnvelopeReceiverQueryBase.Key handling
- Introduced private backing field `_key` for Key property
- Removed dynamic recomputation from Envelope and Receiver
- Ensured Key is only set once during initialization
- Improved null handling and exception safety
2025-08-26 16:49:22 +02:00
Developer 02
c887f857cd fix(EnvelopeReceiverQueryBase): make Envelope and Receiver properties not null and set default value 2025-08-26 16:39:12 +02:00
Developer 02
f114144d34 Fügen das Suffix „-base“ am Ende gängiger Modelle hinzu. 2025-08-26 16:36:52 +02:00
Developer 02
5c09601e3f refactor(ModifyDocStatusCommandBase): update to use EnvelopeReceiverQuery 2025-08-26 16:33:56 +02:00
Developer 02
18b05a3c63 refactor(ReceiverQuery): make properties virtual 2025-08-26 16:26:52 +02:00
Developer 02
ce35b0fea1 refactor(EnvelopeQuery): make properties virtual 2025-08-26 16:26:11 +02:00
Developer 02
7f18cd64c5 feat(EnvelopeReceiverQuery): create query with Key, Envelope and Receiver properties 2025-08-26 16:25:37 +02:00
Developer 02
0083c1b6c1 Create common Envelope and Receiver queries 2025-08-26 14:34:52 +02:00
59e73dbcf0 refactor(CreateHistoryCommand): add Envelope and Receiver queries 2025-08-26 10:36:46 +02:00
f34770931f feat(CreateHistoryCommand): add CreateHistoryCommandHandler with repository integration
- Extend CreateHistoryCommand to implement IRequest<long?>
- Introduce CreateHistoryCommandHandler to handle command via IRepository<EnvelopeHistory>
- Implement async creation and verification of EnvelopeHistory records
2025-08-25 17:33:49 +02:00
78100ef24f create CreateHistoryCommand 2025-08-25 17:22:26 +02:00
99083a68aa move mapping profile to pre dir 2025-08-25 16:50:42 +02:00
0939e57c56 refactor(EnvelopeController): migrate envelope update to MediatR with annotations
- Renamed `Update` action to `CreateOrUpdate`.
- Replaced manual signing logic with `_mediator.SignDocAsync`.
- Added `ExpandoObject` parameter to handle document annotations.
- Improved authorization checks and logging for missing claims.
- Kept legacy `Reject` endpoint intact with obsolete services.
2025-08-25 16:40:46 +02:00
00bdfeb9bb refactor(query): restructure ReceiverAlreadySignedQuery for clarity
- Replaced internal string properties with EnvelopeQuery and ReceiverQuery records
- Updated Key property to encode/decode using the new structured types
- Added overloaded IMediator extension methods IsSignedAsync for better usability
- Simplified ReceiverAlreadySignedQueryHandler to work with the new structure
2025-08-25 16:27:37 +02:00
cced0e5579 refator(SaveDocStatusCommandHandler): use SingleOrDefaultAsync instead of FirstOrDefaultAsync 2025-08-25 15:56:37 +02:00
82150290d2 refactor(Extensions): update SaveDocStatusAsync to use uuid and signature 2025-08-25 15:55:37 +02:00
fb7fd47a2a feat(SaveDocStatusCommand): add IMediator extension methods for saving and signing document status
- Introduced `SaveDocStatusAsync` extension method on IMediator to simplify saving document status
- Added `SignDocAsync` extension method as a shortcut for signing document status
- Refactored `SaveDocStatusCommand` usage to support new mediator extension
2025-08-25 15:43:54 +02:00
20b6b328f5 feat(EGDbContext): Eindeutige Indizes und Beziehungen für die Entitäten „Envelope“, „Receiver“ und „DocumentStatus“ hinzufügen
- Eindeutiger Index für „Envelope.Uuid“ hinzugefügt
- Eindeutige Indizes für „Receiver.Signature“ und „Receiver.EmailAddress“ hinzugefügt
- Beziehungen von „DocumentStatus“ zu „Envelope.Uuid“ und „Receiver“ (Signature und EmailAddress) konfiguriert
- Entitätsbeschränkungen für die Datenintegrität verbessert
2025-08-25 15:32:48 +02:00
fb07d9151f add mapping profiles 2025-08-25 15:20:29 +02:00
a3bc26bd08 feat(SaveDocStatusCommand): enhance SaveDocStatusCommandHandler with flexible envelope & receiver filters
- Added support for filtering by Envelope.Id or Envelope.Uuid
- Added support for filtering by Receiver.Id, Receiver.EmailAddress, or Receiver.Signature
- Throw BadRequestException when required identifiers are missing
- Updated repository queries to combine envelope and receiver filters
2025-08-25 15:02:57 +02:00
e1f793e571 refactor(TaskExtensions): TaskExtensions verallgemeinern, um benutzerdefinierte Ausnahmegeneratoren zu unterstützen
- Feste NotFoundException durch generischen Ausnahmegenerator in ThrowIfNull-Methoden ersetzt.
- Neue Exceptions-Hilfsklasse für die Erstellung gängiger Ausnahmen (NotFound, BadRequest, Forbidden) hinzugefügt.
- Funktionalität der Then-Erweiterungsmethode unverändert beibehalten.
2025-08-25 12:41:27 +02:00
86d8fcda07 chore: update to use DigitalData.Core.Exceptions instead of project exceptions classes 2025-08-25 11:48:29 +02:00
2f8401073f feat(SaveDocStatusCommand): Füge SaveDocStatusCommand und Handler hinzu, um den Dokumentstatus zu erstellen oder zu aktualisieren. 2025-08-25 11:41:08 +02:00
85a855fe64 refactor(ModifyDocStatusCommandBase): remove ChangedWhen-property 2025-08-25 11:18:48 +02:00
996b544633 feat(ModifyDocStatusCommandBase): create abstract class to handle common properties of commands 2025-08-25 11:16:57 +02:00
811656c4ca create UpdateDocStatusCommand 2025-08-25 10:36:35 +02:00
7e90d25f0b create CreateDocStatusCommand 2025-08-25 10:24:15 +02:00
20751aa708 update to remove loadEnvelope-metod 2025-08-23 01:15:51 +02:00
51b96e2a81 move extension extensions dir 2025-08-23 00:57:55 +02:00
7a011930df remvoe sanitzer from views and use extensions instead of keys 2025-08-23 00:54:27 +02:00
a080aaec95 refactor(ViewControllerBase): remvoe sanitzer 2025-08-23 00:17:59 +02:00
d390802305 refactor(HomeController): remove culture binding as viewdata; bind via string localizer 2025-08-23 00:14:43 +02:00
0b33ba0fd8 update to use ClaimsPrincipal instead of ControllerBase 2025-08-22 22:29:51 +02:00
6778d8e3e7 refactor add culture midleware 2025-08-22 22:26:23 +02:00
3394a580f4 refactor(DocumentController): remvoe Open endpoint and its client method 2025-08-22 21:54:25 +02:00
82a63b0dae remove envelopeOldService 2025-08-22 21:44:49 +02:00
e61d626bf3 implement receiver already signed method 2025-08-22 21:38:53 +02:00
bd7c1d4e36 feat(ReceiverAlreadySignedQuery): create with handler 2025-08-22 21:34:33 +02:00
30e2ac602d change LoadEnvelope method 2025-08-22 20:05:53 +02:00
1577440b77 refactor(EnvelopeStatus); arrange naming 2025-08-22 19:34:04 +02:00
68a6a23a20 refactor(TestEnvelopeReceiverController): remove observed inhearence and rename.
- Add related attributes
2025-08-22 19:29:16 +02:00
5e5458d87c fix conflicts after updates 2025-08-22 19:19:59 +02:00
eae83adee4 refactor(ReadEnvelopeReceiverQuery): update response to return list instate of sigel instance 2025-08-22 18:01:31 +02:00
a29785f7c7 feat(EnvelopeReceiverControler): add test endpoint for ReadEnvelopeReceiverQuery 2025-08-22 17:37:55 +02:00
cb641fd33a feat: add TaskExtensions with null and empty result checks
- Introduced ThrowIfNull<T>(Task<T?>) extension method to ensure awaited result is not null, throwing NotFoundException otherwise
- Added ThrowIfNull<T>(Task<IEnumerable<T>?>) extension method to validate collections, throwing NotFoundException if null or empty
- Supports optional custom exception messages for better error context
2025-08-22 16:33:30 +02:00
9434832261 refactor(Application.Contracts): rename Application.Interfaces 2025-08-22 15:42:38 +02:00
b7e19db0f1 update to use Constants.EnvelopeStatus instead of int 2025-08-22 15:26:37 +02:00
290e87048c add logic for status query 2025-08-22 15:24:34 +02:00
c0a5b57668 refactor(ReadEnvelopeQuery): move EnvelopeStatusQuery from ReadEnvelopeReceiverQuery 2025-08-22 14:54:36 +02:00
02ef05f054 feat(envelope-receivers): add query handler for reading envelope receiver with filtering
- Implemented ReadEnvelopeReceiverQueryHandler using MediatR
- Added filtering support for Envelope (Id, Status, Uuid) and Receiver (Id, EmailAddress, Signature)
- Included related navigation properties (Envelope, Documents, Elements, History, User, Receiver)
- Mapped result to EnvelopeReceiverDto using AutoMapper
2025-08-21 18:32:41 +02:00
72134c3d3b add IRepository<EnvelopeReceiver> to ReadEnvelopeReceiverQueryHandler 2025-08-21 17:42:02 +02:00
a4fffaa9b9 init the handler of ReadEnvelopeReceiverQuery 2025-08-21 17:37:37 +02:00
3a62f5317f feat(ReadEnvelopeReceiverQuery): add Key-property 2025-08-21 17:36:18 +02:00
59c93de8b7 feat(ReceiverQuery): merge ReadReceiverQuery 2025-08-21 17:14:45 +02:00
5122a2099d remove ReadReceiverQuery and Read-dir 2025-08-21 17:12:36 +02:00
95ec19d816 rempve Read dir 2025-08-21 17:09:24 +02:00
c7b3d97b2e refactor: remove CreateEnvelopeReceiverResponse 2025-08-21 17:07:22 +02:00
196941f73f remove EnvelopeQuery 2025-08-21 16:56:22 +02:00
415fe646b2 simplify ReadDocumentQuery 2025-08-21 16:52:04 +02:00
f42218802d Standardisierung der Struktur von Mapping-Profildateien 2025-08-21 16:43:50 +02:00
fcf00171de move CreateEnvelopeResponse to CreateEnvelopeCommand file 2025-08-21 16:39:16 +02:00
ca28c4cca4 refactor(CreateEnvelopeResponse): update to use getter-initters 2025-08-21 16:36:34 +02:00
a48e4988d6 Verschieben die Klassen von CreateEnvelopeReceiverDtos nach CreateEnvelopeReceiverCommand. 2025-08-21 16:26:23 +02:00
ec513716ff remove read dir and move files 2025-08-21 16:24:04 +02:00
f39b761412 remove create folder and move the files 2025-08-21 16:04:06 +02:00
70d122d2ff refactor(EnvelopeGenerator.Application.DTOs): Umbenennen in EnvelopeGenerator.Application.Dto 2025-08-21 15:55:15 +02:00
5bc5fcf764 refactor(EnvelopeOldService): remove LoadEnvelopes-method 2025-08-21 15:47:04 +02:00
8db62b41ba refactor(EnvelopeReceiver): remove companyName property 2025-08-21 14:54:48 +02:00
0f27600c5b feat(ReceiverVM): add From method to be able to generate from EnvelopeReceiver 2025-08-21 14:43:56 +02:00
9045655262 fix(Designer): Nicht verfügbare Datenquelle entfernen 2025-08-21 14:18:28 +02:00
5bcac264a7 refactor(CommonServices): updated to use ReveiverVM 2025-08-21 14:13:31 +02:00
c7bf800cd5 feat(ReceiverVM): Handles the combination of the envelope and receiver for common services and submodules as a view model. 2025-08-21 13:52:03 +02:00
5fb4d03ee7 refactor(Receiver): bring related props from EnvelopeReceiver 2025-08-21 13:36:48 +02:00
ac70aaa527 refactor(EnvelopeReceiver): remove not-mapped attribute 2025-08-21 12:44:01 +02:00
e877000b14 fix(Receiver): Entfernen Sie unnötige Eigenschaften und/oder verschieben Sie sie nach EnvelopeReceiver. 2025-08-21 11:54:47 +02:00
305422688e add todo 2025-08-21 11:23:17 +02:00
9c6135d208 refactor(ReadOnlyController): Globale try-catch-Anweisungen entfernen 2025-08-21 11:06:06 +02:00
903e4678ed refactor(Document-, EnvelopeController): Globale try-catch-Anweisungen entfernen 2025-08-21 11:03:41 +02:00
730a318b56 feat(ExceptionHandlingMiddleware): Hinzufügen, um Ausnahmen global zu behandeln 2025-08-21 10:57:34 +02:00
55c0f44954 Refactor(EnvelopeModel): Entfernen Sie den unnötigen try-catch-Block in der Methode GetByUuid des EnvelopeModel. 2025-08-21 10:52:46 +02:00
dbb745338c Entfernen unnötige try-catch-Anweisungen. 2025-08-21 10:47:16 +02:00
419f421d52 refactor(DocResult): remove framework condition_ 2025-08-20 17:43:30 +02:00
e64ac4b5e7 fix(Envelope): remove farmework condition of tfa enabled 2025-08-20 17:21:19 +02:00
d8200993af feat(appsettings.Dev): remove wrong cnn string 2025-08-20 15:04:48 +02:00
ee98142405 refactor(Envelope): EnvelopeType in Type umbenennen 2025-08-19 15:37:12 +02:00
c27337a6f5 refactor(Envelope): make EnvelopeType nullable for only .net 2025-08-19 15:36:28 +02:00
a09ea990ab Revert "refactor(Envelope): remove EnvelopeType-property"
This reverts commit f4c61e3bc7.
2025-08-19 15:34:43 +02:00
05888bc57d revert(EnvelopeDocument): Add Filepath for .net framework
revert(EnvelopeHistory): Add AddedWhen, ActionDate and Comment for .net framework
2025-08-19 15:32:15 +02:00
39af0fe4fd feat(EnvelopeDocument): add System.Drawing-reference for .net-framework 2025-08-19 15:21:13 +02:00
f4c61e3bc7 refactor(Envelope): remove EnvelopeType-property 2025-08-19 15:20:00 +02:00
3f5a584399 refactor: Anpassung der Klassen-Definition für EnvelopeHistory zur Unterstützung von NET und NETFRAMEWORK 2025-08-19 15:11:42 +02:00
6aec854a64 refactor(EnvelopeHistory): Anpassung der Klassen-Definition für EnvelopeHistory zur Unterstützung von NET und NETFRAMEWORK 2025-08-19 15:06:00 +02:00
c360bde103 remove unnecessary references 2025-08-19 15:02:36 +02:00
82d4b0e740 refactor(EnvelopeDocument): Überarbeitung der Klasse EnvelopeDocument für .NET/Framework Kompatibilität
- Hinzugefügt: bedingte Namensraum-Deklaration für NET und NETFRAMEWORK
- Konstruktor eingeführt mit Initialisierung von Elements (nur .NET Framework)
- Entfernt: Property AddedWhen
- Verschoben: FileNameOriginal, IsTempFile, Filename, Filepath, Thumbnail, PageCount
  → nur verfügbar unter .NET Framework
- Entfernt Standardinitialisierung von Elements, stattdessen abhängig von Laufzeitumgebung
2025-08-19 15:02:05 +02:00
12519f06f7 refactor(EnvelopeCertificate): Entfernen Sie die Domäne, das Repository, die DTOs und den Dienst mit allen Schnittstellen. 2025-08-19 14:48:15 +02:00
a2471a0c35 refactor(EmailTemplate): adjust namespace declaration for EmailTemplate to support NET and NETFRAMEWORK 2025-08-19 14:35:35 +02:00
4251a24fe9 refactor(DocumentStatus): DocumentStatus-Klasse für plattformübergreifende Kompilierung angepasst
- Status-Default-Wert in Konstruktor verschoben, nur für NETFRAMEWORK gesetzt
- Klasse für NET/NETFRAMEWORK conditional compilation angepasst
- Unnötige Properties (StatusChangedWhen, AddedWhen, ChangedWhen, Navigation Properties) entfernt
- TODO-Kommentar für Status-Überprüfung erhalten
2025-08-19 14:31:11 +02:00
63a830c8e3 refactor(DocumentReceiverElement): Trenne .NET- und .NET Framework-spezifische Initialisierung in DocumentReceiverElement
- Verschiebe Standardwerte für Id, Required, ReadOnly und AnnotationIndex in den Konstruktor für .NET Framework
- Passe #if-Direktiven an, um Namensräume und NotMapped-Eigenschaften korrekt zu handhaben
- Entferne redundante Standardwert-Zuweisungen bei Property-Deklarationen
- Bereite Klasse für plattformübergreifende Nutzung vor
2025-08-19 14:26:14 +02:00
acee28ffce refactor: add conditional compilation for .NET and .NET Framework 2025-08-19 14:18:01 +02:00
50a541c5bf refactor: Plattform-spezifische Anpassungen an der Receiver-Klasse
- `NotMapped` Properties nur für NETFRAMEWORK verfügbar gemacht
- Klasse und Namespaces für NET- und NETFRAMEWORK-Bedingungen angepasst
- Redundant in NET definierten Code entfernt
2025-08-19 14:14:10 +02:00
d43877db62 refactor(Envelope): Standardwerte in der Envelope-Entity in den Konstruktor verschoben 2025-08-19 14:06:17 +02:00
242e66cd8d refactor(Receiver): make TfaRegDeadline nullable 2025-08-19 12:47:57 +02:00
e44fa0b7bd chore Upg UserManager 2025-08-19 12:13:30 +02:00
4201f7820a refactor(Envelope): Envelope-Entitätseigenschaften für NET/NETFRAMEWORK nullfähig und bedingt machen
- Mehrere DateTime- und int-Eigenschaften in nullfähige Typen geändert
- Bedingte Kompilierung für Message, Title, ContractTypeTranslated und StatusTranslated angewendet
- Einige Standardwerte entfernt, um die Kompatibilität mit verschiedenen Frameworks zu verbessern
- Flexibilität der Entitätszuordnung für Envelope verbessert
2025-08-19 11:12:41 +02:00
e095074c22 chore(Application): Aktualisierung zur Verwendung der bedingten Versionsverwaltung für Abhängigkeiten 2025-08-19 09:43:37 +02:00
412f19547f fix(Envelope): Entfernen das Attribut „NotMapped“. 2025-08-19 09:27:27 +02:00
b8c00884a9 Merge EnvelopeReceiver and EnvelopeReceiverBase 2025-08-13 16:35:51 +02:00
51d49d68ba fix(Envelope): Fügen Nicht-relationalen Eigenschaften NotMapped-Attribute hinzu. 2025-08-13 16:07:26 +02:00
4d5ee2b461 fix(MailParams): make Placeholders Dictionary 2025-08-13 13:03:56 +02:00
8a79ee4126 Stil: BaseController auf Datei-Namensraumformat umstellen 2025-08-12 11:24:38 +02:00
cde9896c01 Merge branch 'master' of http://git.dd:3000/AppStd/EnvelopeGenerator 2025-08-07 17:01:51 +02:00
c7d26a87b0 fix(EnvelopeMailService): Vermeiden Sie gemeinsam genutzte veränderbare Zustände in EnvelopeMailService-Platzhaltern
EnvelopeMailService wurde umgestaltet, um gemeinsam genutzte Veränderungen des _placeholders-Wörterbuchs zu vermeiden.
Die Konfigurationseigenschaft MailParams.Placeholders wurde von einem veränderbaren Dictionary<string, string>
zu einem ImmutableDictionary<string, string> geändert, und _placeholders wird nun bei der Dienstkonstruktion als neues Wörterbuch instanziiert.
2025-08-07 17:01:32 +02:00
17ceb1f72a Update EnvelopeGenerator.Web/wwwroot/README.md 2025-08-06 09:48:34 +02:00
b2c396c3b2 Add EnvelopeGenerator.Web/wwwroot/README.md 2025-08-06 09:45:11 +02:00
365 changed files with 6395 additions and 7235 deletions

View File

@@ -1,4 +1,4 @@
namespace EnvelopeGenerator.Application.Configurations;
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///

View File

@@ -1,4 +1,4 @@
namespace EnvelopeGenerator.Application.Configurations;
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///

View File

@@ -1,4 +1,4 @@
namespace EnvelopeGenerator.Application.Configurations;
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///
@@ -23,5 +23,5 @@ public class DispatcherParams
/// <summary>
/// Default value is string.Empty
/// </summary>
public string EmailAttmt1 { get; init; } = string.Empty;
public string? EmailAttmt1 { get; init; } = null;
}

View File

@@ -1,5 +1,5 @@
using DigitalData.Core.Client.Interface;
namespace EnvelopeGenerator.Application.Configurations;
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
/// https://www.gtx-messaging.com/en/api-docs/sms-rest-api/

View File

@@ -1,4 +1,4 @@
namespace EnvelopeGenerator.Application.Configurations;
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///

View File

@@ -1,7 +1,7 @@
using OtpNet;
using System.Globalization;
namespace EnvelopeGenerator.Application.Configurations;
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs;
namespace EnvelopeGenerator.Application.Common.Dto;
/// <summary>
/// Data Transfer Object representing configuration settings.

View File

@@ -1,12 +1,12 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs;
namespace EnvelopeGenerator.Application.Common.Dto;
/// <summary>
/// Data Transfer Object representing a document within an envelope, including optional binary data and form elements.
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public class EnvelopeDocumentDto
public class DocumentDto
{
/// <summary>
/// Gets or sets the unique identifier of the document.
@@ -31,5 +31,5 @@ public class EnvelopeDocumentDto
/// <summary>
/// Gets or sets the collection of elements associated with the document for receiver interactions, if any.
/// </summary>
public IEnumerable<DocumentReceiverElementDto>? Elements { get; set; }
public IEnumerable<SignatureDto>? Elements { get; set; }
}

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using EnvelopeGenerator.Domain.Constants;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs;
namespace EnvelopeGenerator.Application.Common.Dto;
/// <summary>
/// Data Transfer Object representing the status of a document for a specific receiver.
@@ -26,7 +27,7 @@ public class DocumentStatusDto
/// <summary>
/// Gets or sets the current status code.
/// </summary>
public int Status { get; set; }
public EnvelopeStatus Status { get; set; }
/// <summary>
/// Gets or sets the timestamp when the status was changed.

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs
namespace EnvelopeGenerator.Application.Common.Dto
{
/// <summary>
///

View File

@@ -1,9 +1,10 @@
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
using DigitalData.UserManager.Application.DTOs.User;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs;
namespace EnvelopeGenerator.Application.Common.Dto;
/// <summary>
///
@@ -24,7 +25,7 @@ public record EnvelopeDto
/// <summary>
///
/// </summary>
public int Status { get; set; }
public required EnvelopeStatus Status { get; set; }
/// <summary>
/// Default value is string.Empty
@@ -116,5 +117,5 @@ public record EnvelopeDto
/// <summary>
///
/// </summary>
public IEnumerable<EnvelopeDocumentDto>? Documents { get; set; }
public IEnumerable<DocumentDto>? Documents { get; set; }
}

View File

@@ -1,14 +1,25 @@
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
using EnvelopeGenerator.Application.Common.Dto.Receiver;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
/// <summary>
///
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public record EnvelopeReceiverBasicDto
public record EnvelopeReceiverDto
{
/// <summary>
///
/// </summary>
public EnvelopeDto? Envelope { get; set; }
/// <summary>
///
/// </summary>
public ReceiverDto? Receiver { get; set; }
/// <summary>
///
/// </summary>

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
/// <summary>
///

View File

@@ -2,7 +2,7 @@
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
/// <summary>
///

View File

@@ -1,7 +1,8 @@
using EnvelopeGenerator.Application.DTOs.Receiver;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Dto.Receiver;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
/// <summary>
/// Represents a read-only Data Transfer Object (DTO) for an envelope receiver.
@@ -59,5 +60,5 @@ public class EnvelopeReceiverReadOnlyDto
/// <summary>
/// Gets or inits the associated receiver details.
/// </summary>
public ReceiverReadDto? Receiver { get; set; }
public ReceiverDto? Receiver { get; set; }
}

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
/// <summary>
/// Data Transfer Object for updating a read-only envelope receiver.

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs;
namespace EnvelopeGenerator.Application.Common.Dto;
/// <summary>
/// Data Transfer Object representing a type of envelope with its configuration settings.

View File

@@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeHistory;
namespace EnvelopeGenerator.Application.Common.Dto.History;
/// <summary>
/// Data Transfer Object for creating a new envelope history record.
/// </summary>
public class EnvelopeHistoryCreateDto
public class HistoryCreateDto
{
/// <summary>
/// Gets or sets the identifier of the envelope.

View File

@@ -1,13 +1,13 @@
using DigitalData.UserManager.Application.DTOs.User;
using EnvelopeGenerator.Application.DTOs.Receiver;
using static EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Dto.Receiver;
using EnvelopeGenerator.Domain.Constants;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeHistory;
namespace EnvelopeGenerator.Application.Common.Dto.History;
/// <summary>
/// Data Transfer Object representing the history of an envelope, including status, sender, receiver, and related metadata.
/// </summary>
public record EnvelopeHistoryDto
public record HistoryDto
{
/// <summary>
/// Unique identifier for the envelope history entry.
@@ -25,9 +25,19 @@ public record EnvelopeHistoryDto
public required string UserReference { get; set; }
/// <summary>
/// Status code of the envelope at this history point.
/// Include code of the envelope at this history point.
/// </summary>
public int Status { get; set; }
public EnvelopeStatus Status { get; set; }
/// <summary>
/// Type of reference for this history entry.
/// </summary>
public ReferenceType ReferenceType => ((int)Status).ToString().FirstOrDefault() switch
{
'1' => ReferenceType.Sender,
'2' => ReferenceType.Receiver,
_ => ReferenceType.System,
};
/// <summary>
/// Human-readable name of the status.
@@ -52,12 +62,7 @@ public record EnvelopeHistoryDto
/// <summary>
/// Information about the receiver associated with this history entry.
/// </summary>
public ReceiverReadDto? Receiver { get; set; }
/// <summary>
/// Type of reference for this history entry.
/// </summary>
public ReferenceType ReferenceType { get; set; }
public ReceiverDto? Receiver { get; set; }
/// <summary>
/// Optional comment related to this history entry.

View File

@@ -1,13 +1,13 @@
using AutoMapper;
using EnvelopeGenerator.Application.DTOs.EnvelopeHistory;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Application.DTOs.Messaging;
using EnvelopeGenerator.Application.DTOs.Receiver;
using EnvelopeGenerator.Application.Extensions;
using EnvelopeGenerator.Application.Common.Dto.History;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
using EnvelopeGenerator.Application.Common.Dto.Messaging;
using EnvelopeGenerator.Application.Common.Dto.Receiver;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.DTOs;
namespace EnvelopeGenerator.Application.Common.Dto;
/// <summary>
/// Represents the AutoMapper profile configuration for mapping between
@@ -23,38 +23,31 @@ public class MappingProfile : Profile
{
// Entity to DTO mappings
CreateMap<Config, ConfigDto>();
CreateMap<DocumentReceiverElement, DocumentReceiverElementDto>();
CreateMap<Signature, SignatureDto>();
CreateMap<DocumentStatus, DocumentStatusDto>();
CreateMap<EmailTemplate, EmailTemplateDto>();
CreateMap<Envelope, EnvelopeDto>();
CreateMap<EnvelopeCertificate, EnvelopeCertificateDto>();
CreateMap<EnvelopeDocument, EnvelopeDocumentDto>();
CreateMap<Domain.Entities.EnvelopeHistory, EnvelopeHistoryDto>();
CreateMap<Domain.Entities.EnvelopeHistory, EnvelopeHistoryCreateDto>();
CreateMap<Document, DocumentDto>();
CreateMap<Domain.Entities.History, HistoryDto>();
CreateMap<Domain.Entities.History, HistoryCreateDto>();
CreateMap<Domain.Entities.EnvelopeReceiver, EnvelopeReceiverDto>();
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.Receiver, ReceiverDto>();
CreateMap<Domain.Entities.EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
// DTO to Entity mappings
CreateMap<ConfigDto, Config>();
CreateMap<DocumentReceiverElementDto, DocumentReceiverElement>();
CreateMap<SignatureDto, Signature>();
CreateMap<DocumentStatusDto, DocumentStatus>();
CreateMap<EmailTemplateDto, EmailTemplate>();
CreateMap<EnvelopeDto, Envelope>();
CreateMap<EnvelopeCertificateDto, EnvelopeCertificate>();
CreateMap<EnvelopeDocumentDto, EnvelopeDocument>();
CreateMap<EnvelopeHistoryDto, Domain.Entities.EnvelopeHistory>();
CreateMap<EnvelopeHistoryCreateDto, Domain.Entities.EnvelopeHistory>();
CreateMap<DocumentDto, Document>();
CreateMap<HistoryDto, Domain.Entities.History>();
CreateMap<HistoryCreateDto, Domain.Entities.History>();
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<EnvelopeReceiverBase, EnvelopeReceiverBasicDto>();
CreateMap<ReceiverDto, Domain.Entities.Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore());
CreateMap<EnvelopeReceiverReadOnlyCreateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, Domain.Entities.EnvelopeReceiverReadOnly>();

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs.Messaging;
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
/// <summary>
///

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs.Messaging;
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
/// <summary>
///

View File

@@ -1,14 +1,14 @@
using EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Application.DTOs.Receiver;
namespace EnvelopeGenerator.Application.Common.Dto.Receiver;
/// <summary>
///
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public class ReceiverReadDto
public class ReceiverDto
{
/// <summary>
///
@@ -25,6 +25,12 @@ public class ReceiverReadDto
/// </summary>
public required string Signature { get; set; }
/// <summary>
///
/// </summary>
[JsonIgnore]
public string? TotpSecretkey { get; set; }
/// <summary>
///
/// </summary>
@@ -34,18 +40,13 @@ public class ReceiverReadDto
///
/// </summary>
[JsonIgnore]
public IEnumerable<EnvelopeReceiverBasicDto>? EnvelopeReceivers { get; set; }
public IEnumerable<EnvelopeReceiverDto>? EnvelopeReceivers { get; set; }
/// <summary>
///
/// </summary>
public string? LastUsedName => EnvelopeReceivers?.LastOrDefault()?.Name;
/// <summary>
///
/// </summary>
public string? TotpSecretkey { get; set; } = null;
/// <summary>
///
/// </summary>

View File

@@ -1,12 +1,12 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs;
namespace EnvelopeGenerator.Application.Common.Dto;
/// <summary>
/// Data Transfer Object representing a positioned element assigned to a document receiver.
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public class DocumentReceiverElementDto
public class SignatureDto
{
/// <summary>
/// Gets or sets the unique identifier of the element.
@@ -86,10 +86,10 @@ public class DocumentReceiverElementDto
/// <summary>
/// Gets or sets the top position of the element (in layout terms).
/// </summary>
public double Top { get; set; }
public double Top => Y;
/// <summary>
/// Gets or sets the left position of the element (in layout terms).
/// </summary>
public double Left { get; set; }
public double Left => X;
}

View File

@@ -0,0 +1,27 @@
namespace EnvelopeGenerator.Application.Common;
/// <summary>
///
/// </summary>
public enum EnvelopeFlag
{
/// <summary>
///
/// </summary>
EnvelopeOrReceiverNonexists,
/// <summary>
///
/// </summary>
NonDecodableEnvelopeReceiverId,
/// <summary>
///
/// </summary>
WrongEnvelopeReceiverId,
/// <summary>
///
/// </summary>
AccessCodeNull
}

View File

@@ -1,6 +1,6 @@
using Microsoft.Extensions.Caching.Distributed;
namespace EnvelopeGenerator.Application.Extensions;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
///

View File

@@ -0,0 +1,182 @@
using EnvelopeGenerator.Domain.Constants;
using System.Text;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
///
/// </summary>
public static class DecodingExtensions
{
/// <summary>
/// Validates whether a given string is a correctly formatted Base-64 encoded string.
/// </summary>
/// <remarks>
/// This method checks the string for proper Base-64 formatting, which includes validating
/// the length of the string (must be divisible by 4). It also checks each character to ensure
/// it belongs to the Base-64 character set (A-Z, a-z, 0-9, '+', '/', and '=' for padding).
/// The method ensures that padding characters ('=') only appear at the end of the string and
/// are in a valid configuration (either one '=' at the end if the string's length % 4 is 3,
/// or two '==' if the length % 4 is 2).
/// </remarks>
/// <param name="input">The Base-64 encoded string to validate.</param>
/// <returns>
/// <c>true</c> if the string is a valid Base-64 encoded string; otherwise, <c>false</c>.
/// </returns>
/// <example>
/// <code>
/// string testString = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnk=";
/// bool isValid = IsValidBase64String(testString);
/// Console.WriteLine(isValid); // Output: true
/// </code>
/// </example>
public static bool IsBase64String(this string input)
{
// Check if the string is null or empty
if (string.IsNullOrEmpty(input))
{
return false;
}
// Replace valid base-64 padding
input = input.Trim();
int mod4 = input.Length % 4;
if (mod4 > 0)
{
// Base-64 string lengths should be divisible by 4
return false;
}
// Check each character to ensure it is valid base-64
foreach (char c in input)
{
if (!char.IsLetterOrDigit(c) && c != '+' && c != '/' && c != '=')
{
// Invalid character detected
return false;
}
}
// Ensure no invalid padding scenarios exist
if (input.EndsWith("==") && input.Length % 4 == 0 ||
input.EndsWith("=") && input.Length % 4 == 3)
{
return true;
}
return input.IndexOf('=') == -1; // No padding allowed except at the end
}
/// <summary>
///
/// </summary>
/// <param name="encodedKey"></param>
/// <param name="decodedKeys"></param>
/// <returns></returns>
public static bool TryDecode(this string encodedKey, out string[] decodedKeys)
{
try
{
byte[] bytes = Convert.FromBase64String(encodedKey);
string decodedString = Encoding.UTF8.GetString(bytes);
decodedKeys = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
return true;
}
catch(ArgumentNullException) { }
catch (FormatException) { }
catch(ArgumentException) { }
decodedKeys = Array.Empty<string>();
return false;
}
/// <summary>
///
/// </summary>
/// <param name="decodedKeys"></param>
/// <returns></returns>
public static EncodeType GetEncodeType(this string[] decodedKeys) => decodedKeys.Length switch
{
2 => EncodeType.EnvelopeReceiver,
3 => long.TryParse(decodedKeys[1], out var _) ? EncodeType.EnvelopeReceiverReadOnly : EncodeType.Undefined,
_ => EncodeType.Undefined,
};
/// <summary>
///
/// </summary>
/// <param name="decodedKeys"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static (string? EnvelopeUuid, string? ReceiverSignature) ParseEnvelopeReceiverId(this string[] decodedKeys)
=> decodedKeys.GetEncodeType() == EncodeType.EnvelopeReceiver
? (EnvelopeUuid: decodedKeys[0], ReceiverSignature: decodedKeys[1])
: throw new InvalidOperationException("Attempted to convert a decoded other than type EnvelopeReceiver to EnvelopeReceiver.");
/// <summary>
///
/// </summary>
/// <param name="decodedKeys"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static long ParseReadOnlyId(this string[] decodedKeys)
=> decodedKeys.GetEncodeType() == EncodeType.EnvelopeReceiverReadOnly
? long.Parse(decodedKeys[1])
: throw new InvalidOperationException("Attempted to convert a decoded other than type EnvelopeReceiver to EnvelopeReceiver. ");
/// <summary>
/// Decodes the envelope receiver ID and extracts the envelope UUID and receiver signature.
/// </summary>
/// <param name="envelopeReceiverId">The base64 encoded string containing the envelope UUID and receiver signature.</param>
/// <returns>A tuple containing the envelope UUID and receiver signature.</returns>
public static (string? EnvelopeUuid, string? ReceiverSignature) DecodeEnvelopeReceiverId(this string envelopeReceiverId)
{
if (!envelopeReceiverId.IsBase64String())
{
return (null, null);
}
byte[] bytes = Convert.FromBase64String(envelopeReceiverId);
string decodedString = Encoding.UTF8.GetString(bytes);
string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
if (parts.Length > 1)
return (EnvelopeUuid: parts[0], ReceiverSignature: parts[1]);
else
return (string.Empty, string.Empty);
}
/// <summary>
///
/// </summary>
/// <param name="envelopeReceiverReadOnlyId"></param>
/// <returns></returns>
public static long? DecodeEnvelopeReceiverReadOnlyId(this string envelopeReceiverReadOnlyId)
{
if (!envelopeReceiverReadOnlyId.IsBase64String())
{
return null;
}
byte[] bytes = Convert.FromBase64String(envelopeReceiverReadOnlyId);
string decodedString = Encoding.UTF8.GetString(bytes);
string[] parts = decodedString.Split(new string[] { "::" }, StringSplitOptions.None);
if (parts.Length > 2)
return long.TryParse(parts[1], out long readOnlyId) ? readOnlyId : null;
else
return null;
}
/// <summary>
/// Gets the envelope UUID from the decoded envelope receiver ID.
/// </summary>
/// <param name="envelopeReceiverId">The base64 encoded string to decode.</param>
/// <returns>The envelope UUID.</returns>
public static string? GetEnvelopeUuid(this string envelopeReceiverId) => envelopeReceiverId.DecodeEnvelopeReceiverId().EnvelopeUuid;
/// <summary>
/// Gets the receiver signature from the decoded envelope receiver ID.
/// </summary>
/// <param name="envelopeReceiverId">The base64 encoded string to decode.</param>
/// <returns>The receiver signature.</returns>
public static string? GetReceiverSignature(this string envelopeReceiverId) => envelopeReceiverId.DecodeEnvelopeReceiverId().ReceiverSignature;
}

View File

@@ -0,0 +1,38 @@
using System.Text;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
/// Provides extension methods for decoding and extracting information from an envelope receiver ID.
/// </summary>
public static class EncodingExtensions
{
/// <summary>
///
/// </summary>
/// <param name="readOnlyId"></param>
/// <returns></returns>
public static string ToEnvelopeKey(this long readOnlyId)
{
//The random number is used as a salt to increase security but it is not saved in the database.
string combinedString = $"{Random.Shared.Next()}::{readOnlyId}::{Random.Shared.Next()}";
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
string base64String = Convert.ToBase64String(bytes);
return base64String;
}
/// <summary>
///
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static string ToEnvelopeKey(this (string envelopeUuid, string receiverSignature) input)
{
string combinedString = $"{input.envelopeUuid}::{input.receiverSignature}";
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
string base64String = Convert.ToBase64String(bytes);
return base64String;
}
}

View File

@@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Text;
namespace EnvelopeGenerator.Extensions
namespace EnvelopeGenerator.Application.Common.Extensions
{
public static class LoggerExtensions
{

View File

@@ -1,6 +1,6 @@
using EnvelopeGenerator.Application.DTOs.Messaging;
using EnvelopeGenerator.Application.Common.Dto.Messaging;
namespace EnvelopeGenerator.Application.Extensions;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
/// Provides extension methods for common mapping and conversion operations.

View File

@@ -1,11 +1,24 @@
using Microsoft.Extensions.Caching.Memory;
using EnvelopeGenerator.Application.Common.Extensions;
using Microsoft.Extensions.Caching.Memory;
namespace EnvelopeGenerator.Extensions;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
///
/// </summary>
public static class MemoryCacheExtensions
{
private static readonly Guid BaseId = Guid.NewGuid();
/// <summary>
///
/// </summary>
/// <typeparam name="TEnum"></typeparam>
/// <param name="memoryCache"></param>
/// <param name="key"></param>
/// <param name="ignores"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static IDictionary<string, int> GetEnumAsDictionary<TEnum>(this IMemoryCache memoryCache, string key = "", params object[] ignores)
where TEnum : Enum
=> memoryCache.GetOrCreate(BaseId + typeof(TEnum).FullName + key, _ =>

View File

@@ -0,0 +1,125 @@
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Query;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Domain.Interfaces;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
///
/// </summary>
public static class QueryExtensions
{
/// <summary>
///
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="root"></param>
/// <param name="query"></param>
/// <param name="notnull"></param>
/// <returns></returns>
/// <exception cref="BadRequestException"></exception>
public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> root, EnvelopeQueryBase query, bool notnull = true)
where TEntity : IHasEnvelope
{
if (query.Id is not null)
root = root.Where(e => e.Envelope!.Id == query.Id);
else if (query.Uuid is not null)
root = root.Where(e => e.Envelope!.Uuid == query.Uuid);
else if (notnull)
throw new BadRequestException(
"Either Envelope Id or Envelope Uuid must be provided in the query."
);
return root;
}
/// <summary>
///
/// </summary>
/// <param name="root"></param>
/// <param name="query"></param>
/// <param name="notnull"></param>
/// <returns></returns>
/// <exception cref="BadRequestException"></exception>
public static IQueryable<Envelope> Where(this IQueryable<Envelope> root, EnvelopeQueryBase query, bool notnull = true)
{
if (query.Id is not null)
root = root.Where(e => e.Id == query.Id);
else if (query.Uuid is not null)
root = root.Where(e => e.Uuid == query.Uuid);
else if (notnull)
throw new BadRequestException(
"Either Envelope Id or Envelope Uuid must be provided in the query."
);
return root;
}
/// <summary>
///
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="root"></param>
/// <param name="query"></param>
/// <param name="notnull"></param>
/// <returns></returns>
/// <exception cref="BadRequestException"></exception>
public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> root, ReceiverQueryBase query, bool notnull = true)
where TEntity : IHasReceiver
{
if (query.Id is not null)
root = root.Where(e => e.Receiver!.Id == query.Id);
else if (query.EmailAddress is not null)
root = root.Where(e => e.Receiver!.EmailAddress == query.EmailAddress);
else if (query.Signature is not null)
root = root.Where(e => e.Receiver!.Signature == query.Signature);
else if (notnull)
throw new BadRequestException(
"Receiver must have at least one identifier (Id, EmailAddress, or Signature)."
);
return root;
}
/// <summary>
///
/// </summary>
/// <param name="root"></param>
/// <param name="query"></param>
/// <param name="notnull"></param>
/// <returns></returns>
/// <exception cref="BadRequestException"></exception>
public static IQueryable<Receiver> Where(this IQueryable<Receiver> root, ReceiverQueryBase query, bool notnull = true)
{
if (query.Id is not null)
root = root.Where(e => e.Id == query.Id);
else if (query.EmailAddress is not null)
root = root.Where(e => e.EmailAddress == query.EmailAddress);
else if (query.Signature is not null)
root = root.Where(e => e.Signature == query.Signature);
else if (notnull)
throw new BadRequestException(
"Receiver must have at least one identifier (Id, EmailAddress, or Signature)."
);
return root;
}
/// <summary>
///
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TEnvelopeQuery"></typeparam>
/// <typeparam name="TReceiverQuery"></typeparam>
/// <param name="root"></param>
/// <param name="query"></param>
/// <param name="notnull"></param>
/// <returns></returns>
public static IQueryable<TEntity> Where<TEntity, TEnvelopeQuery, TReceiverQuery>(this IQueryable<TEntity> root, EnvelopeReceiverQueryBase<TEnvelopeQuery, TReceiverQuery> query, bool notnull = true)
where TEntity : IHasEnvelope, IHasReceiver
where TEnvelopeQuery : EnvelopeQueryBase, new()
where TReceiverQuery : ReceiverQueryBase, new()
=> root.Where(query.Envelope, notnull).Where(query.Receiver, notnull);
}

View File

@@ -1,6 +1,6 @@
using OtpNet;
namespace EnvelopeGenerator.Extensions
namespace EnvelopeGenerator.Application.Common.Extensions
{
public static class StringExtension
{

View File

@@ -0,0 +1,78 @@
using DigitalData.Core.Exceptions;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
/// Extension methods for tasks
/// </summary>
public static class TaskExtensions
{
/// <summary>
/// Awaits the specified task and ensures that the result is not <c>null</c>.
/// If the result is <c>null</c>, the exception created by factory-method is thrown.
/// </summary>
/// <typeparam name="T">The type of the result.</typeparam>
/// <typeparam name="TException">The type of the exception.</typeparam>
/// <param name="task">The task to await.</param>
/// <param name="factory">Exception provider</param>
/// <returns>The awaited result if not <c>null</c>.</returns>
/// <exception>Thrown if the result is <c>null</c>.</exception>
public static async Task<T> ThrowIfNull<T, TException>(this Task<T?> task, Func<TException> factory) where TException : Exception
{
var result = await task;
return result ?? throw factory();
}
/// <summary>
/// Awaits the specified task and ensures that the result is not <c>empty</c>.
/// If the result contains no elements, the exception created by factory-method is thrown.
/// </summary>
/// <typeparam name="T">The element type of the collection.</typeparam>
/// <typeparam name="TException">The type of the exception.</typeparam>
/// <param name="task">The task to await.</param>
/// <param name="factory">Exception provider</param>
/// <returns>The awaited collection if it is not <c>null</c> or empty.</returns>
/// <exception cref="NotFoundException">Thrown if the result is <c>null</c> or empty.</exception>
public static async Task<IEnumerable<T>> ThrowIfEmpty<T, TException>(this Task<IEnumerable<T>> task, Func<TException> factory) where TException : Exception
{
var result = await task;
return result?.Any() ?? false ? result : throw factory();
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="I"></typeparam>
/// <param name="task"></param>
/// <param name="act"></param>
/// <returns></returns>
public static async Task<I> Then<T, I>(this Task<T> task, Func<T, I> act)
{
var res = await task;
return act(res);
}
}
/// <summary>
///
/// </summary>
public static class Exceptions
{
/// <summary>
///
/// </summary>
public static NotFoundException NotFound() => new();
/// <summary>
///
/// </summary>
/// <returns></returns>
public static BadRequestException BadRequest() => new();
/// <summary>
///
/// </summary>
/// <returns></returns>
public static ForbiddenException Forbidden() => new();
}

View File

@@ -2,7 +2,7 @@
using Microsoft.Extensions.Localization;
using System.Text.Encodings.Web;
namespace EnvelopeGenerator.Extensions
namespace EnvelopeGenerator.Application.Common.Extensions
{
public static class XSSExtensions
{

View File

@@ -0,0 +1,55 @@
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
using Newtonsoft.Json;
using System.Dynamic;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned;
/// <summary>
///
/// </summary>
/// <param name="Original"></param>
public record DocSignedNotification(EnvelopeReceiverDto Original) : EnvelopeReceiverDto(Original), INotification, ISendMailNotification
{
/// <summary>
///
/// </summary>
public required ExpandoObject Annotations { get; init; }
/// <summary>
///
/// </summary>
public EmailTemplateType TemplateType => EmailTemplateType.DocumentSigned;
/// <summary>
///
/// </summary>
public string EmailAddress => Receiver?.EmailAddress
?? throw new InvalidOperationException($"Receiver is null." +
$"DocSignedNotification:\n{JsonConvert.SerializeObject(this, Format.Json.ForDiagnostics)}");
}
/// <summary>
///
/// </summary>
public static class DocSignedNotificationExtensions
{
/// <summary>
/// Converts an <see cref="EnvelopeReceiverDto"/> to a <see cref="DocSignedNotification"/>.
/// </summary>
/// <param name="dto">The DTO to convert.</param>
/// <param name="annotations"></param>
/// <returns>A new <see cref="DocSignedNotification"/> instance.</returns>
public static DocSignedNotification ToDocSignedNotification(this EnvelopeReceiverDto dto, ExpandoObject annotations)
=> new(dto) { Annotations = annotations };
/// <summary>
/// Asynchronously converts a <see cref="Task{EnvelopeReceiverDto}"/> to a <see cref="DocSignedNotification"/>.
/// </summary>
/// <param name="dtoTask">The task that returns the DTO to convert.</param>
/// <param name="annotations"></param>
/// <returns>A task that represents the asynchronous conversion operation.</returns>
public static async Task<DocSignedNotification?> ToDocSignedNotification(this Task<EnvelopeReceiverDto?> dtoTask, ExpandoObject annotations)
=> await dtoTask is EnvelopeReceiverDto dto ? new(dto) { Annotations = annotations } : null;
}

View File

@@ -0,0 +1,40 @@
using EnvelopeGenerator.Application.Common.Notifications.DocSigned;
using EnvelopeGenerator.Application.DocStatus.Commands;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
using Newtonsoft.Json;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
/// <summary>
///
/// </summary>
public class AnnotationHandler : INotificationHandler<DocSignedNotification>
{
private readonly ISender _sender;
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
public AnnotationHandler(ISender sender)
{
_sender = sender;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
{
await _sender.Send(new SaveDocStatusCommand()
{
Envelope = new() { Id = notification.EnvelopeId },
Receiver = new() { Id = notification.ReceiverId},
Value = JsonConvert.SerializeObject(notification.Annotations, Format.Json.ForAnnotations)
}, cancel);
}
}

View File

@@ -0,0 +1,45 @@
using EnvelopeGenerator.Application.Common.Notifications.DocSigned;
using EnvelopeGenerator.Application.Histories.Commands;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
using Newtonsoft.Json;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
/// <summary>
///
/// </summary>
public class HistoryHandler : INotificationHandler<DocSignedNotification>
{
private readonly ISender _sender;
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
public HistoryHandler(ISender sender)
{
_sender = sender;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
{
if(notification.Receiver is null)
if (notification.Receiver is null)
throw new InvalidOperationException($"Receiver information is missing in the notification. DocSignedNotification:\n {JsonConvert.SerializeObject(notification, Format.Json.ForDiagnostics)}");
await _sender.Send(new CreateHistoryCommand()
{
EnvelopeId = notification.EnvelopeId,
UserReference = notification.Receiver.EmailAddress,
Status = EnvelopeStatus.DocumentSigned,
Comment = JsonConvert.SerializeObject(notification.Annotations, Format.Json.ForAnnotations)
}, cancel);
}
}

View File

@@ -0,0 +1,50 @@
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.Extensions.Options;
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
/// <summary>
///
/// </summary>
public class SendSignedMailHandler : SendMailHandler<DocSignedNotification>
{
/// <summary>
///
/// </summary>
/// <param name="tempRepo"></param>
/// <param name="emailOutRepo"></param>
/// <param name="mailParamsOptions"></param>
/// <param name="dispatcherParamsOptions"></param>
public SendSignedMailHandler(IRepository<EmailTemplate> tempRepo, IRepository<EmailOut> emailOutRepo, IOptions<MailParams> mailParamsOptions, IOptions<DispatcherParams> dispatcherParamsOptions) : base(tempRepo, emailOutRepo, mailParamsOptions, dispatcherParamsOptions)
{
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="emailOut"></param>
protected override void ConfigureEmailOut(DocSignedNotification notification, EmailOut emailOut)
{
emailOut.ReferenceString = notification.EmailAddress;
emailOut.ReferenceId = notification.ReceiverId;
}
/// <summary>
/// </summary>
/// <param name="notification"></param>
/// <returns></returns>
protected override Dictionary<string, string> CreatePlaceHolders(DocSignedNotification notification)
{
var placeHolders = new Dictionary<string, string>()
{
{ "[NAME_RECEIVER]", notification.Name ?? string.Empty },
{ "[DOCUMENT_TITLE]", notification.Envelope?.Title ?? string.Empty },
};
return placeHolders;
}
}

View File

@@ -0,0 +1,152 @@
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace EnvelopeGenerator.Application.Common.Notifications;
/// <summary>
///
/// </summary>
public interface ISendMailNotification : INotification
{
/// <summary>
///
/// </summary>
public EmailTemplateType TemplateType { get; }
/// <summary>
///
/// </summary>
public string EmailAddress { get; }
}
/// <summary>
///
/// </summary>
public abstract class SendMailHandler<TNotification> : INotificationHandler<TNotification>
where TNotification : ISendMailNotification
{
/// <summary>
///
/// </summary>
protected readonly IRepository<EmailTemplate> TempRepo;
/// <summary>
///
/// </summary>
protected readonly IRepository<EmailOut> EmailOutRepo;
/// <summary>
///
/// </summary>
protected abstract Dictionary<string, string> CreatePlaceHolders(TNotification notification);
/// <summary>
///{ "[MESSAGE]", notification.Message },<br/>
///{ "[DOCUMENT_ACCESS_CODE]", notification.ReceiverAccessCode },<br/>
///{ "[REASON]", pReason }<br/>
///{ "[NAME_SENDER]", notification.Envelope.User?.FullName},<br/>
///{ "[NAME_PORTAL]", DispatcherParams. },<br/>
///{ "[SIGNATURE_TYPE]", "signieren" },<br/>
///{ "[LINK_TO_DOCUMENT]", notification.SignatureLink },<br/>
///{ "[LINK_TO_DOCUMENT_TEXT]", $"{notification.SignatureLink.Truncate(40)}.." },
/// </summary>
protected readonly MailParams MailParams;
/// <summary>
///
/// </summary>
protected readonly DispatcherParams DispatcherParams;
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="emailOut"></param>
protected abstract void ConfigureEmailOut(TNotification notification, EmailOut emailOut);
/// <summary>
///
/// </summary>
/// <param name="tempRepo"></param>
/// <param name="emailOutRepo"></param>
/// <param name="mailParamsOptions"></param>
/// <param name="dispatcherParamsOptions"></param>
protected SendMailHandler(IRepository<EmailTemplate> tempRepo, IRepository<EmailOut> emailOutRepo, IOptions<MailParams> mailParamsOptions, IOptions<DispatcherParams> dispatcherParamsOptions)
{
TempRepo = tempRepo;
EmailOutRepo = emailOutRepo;
MailParams = mailParamsOptions.Value;
DispatcherParams = dispatcherParamsOptions.Value;
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public virtual async Task Handle(TNotification notification, CancellationToken cancel)
{
var placeHolders = CreatePlaceHolders(notification);
var temp = await TempRepo
.ReadOnly()
.SingleOrDefaultAsync(x => x.Name == notification.TemplateType.ToString(), cancel)
?? throw new InvalidOperationException($"Receiver information is missing in the notification." +
$"{typeof(TNotification)}:\n {JsonConvert.SerializeObject(notification, Format.Json.ForDiagnostics)}");
temp.Subject = ReplacePlaceHolders(temp.Subject, placeHolders, MailParams.Placeholders);
temp.Body = ReplacePlaceHolders(temp.Body, placeHolders, MailParams.Placeholders);
var emailOut = new EmailOut
{
EmailAddress = notification.EmailAddress,
EmailBody = TextToHtml(temp.Body),
EmailSubj = temp.Subject,
AddedWhen = DateTime.UtcNow,
AddedWho = DispatcherParams.AddedWho,
SendingProfile = DispatcherParams.SendingProfile,
ReminderTypeId = DispatcherParams.ReminderTypeId,
EmailAttmt1 = DispatcherParams.EmailAttmt1,
WfId = (int)EnvelopeStatus.MessageConfirmationSent,
};
ConfigureEmailOut(notification, emailOut);
await EmailOutRepo.CreateAsync(emailOut, cancel);
}
private static string ReplacePlaceHolders(string text, params Dictionary<string, string>[] placeHoldersList)
{
foreach (var placeHolders in placeHoldersList)
foreach (var ph in placeHolders)
text = text.Replace(ph.Key, ph.Value);
return text;
}
private static string TextToHtml(string input)
{
if (string.IsNullOrEmpty(input)) return "";
// HTML encoding special characters
string encoded = System.Net.WebUtility.HtmlEncode(input);
// Convert tabs to &nbsp; (4 non-breaking spaces)
encoded = encoded.Replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;");
// Convert line breaks to <br />
encoded = encoded.Replace("\r\n", "<br />"); // Windows
encoded = encoded.Replace("\r", "<br />"); // Mac old
encoded = encoded.Replace("\n", "<br />"); // Unix/Linux
return encoded;
}
}

View File

@@ -0,0 +1,17 @@
namespace EnvelopeGenerator.Application.Common.Query;
/// <summary>
/// Repräsentiert eine Abfrage für Umschläge.
/// </summary>
public record EnvelopeQueryBase
{
/// <summary>
/// Die eindeutige Kennung des Umschlags.
/// </summary>
public virtual int? Id { get; set; }
/// <summary>
/// Die universell eindeutige Kennung des Umschlags.
/// </summary>
public virtual string? Uuid { get; set; }
}

View File

@@ -0,0 +1,62 @@
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Common.Extensions;
namespace EnvelopeGenerator.Application.Common.Query;
/// <summary>
///
/// </summary>
public record EnvelopeReceiverQueryBase : EnvelopeReceiverQueryBase<EnvelopeQueryBase, ReceiverQueryBase>;
/// <summary>
///
/// </summary>
/// <typeparam name="TEnvelopeQuery"></typeparam>
/// <typeparam name="TReceiverQuery"></typeparam>
public record EnvelopeReceiverQueryBase<TEnvelopeQuery, TReceiverQuery>
where TEnvelopeQuery : EnvelopeQueryBase, new()
where TReceiverQuery : ReceiverQueryBase, new()
{
private string? _key;
/// <summary>
///
/// </summary>
public virtual string? Key
{
get => _key;
set
{
if (value is null)
{
_key = null;
return;
}
(string? EnvelopeUuid, string? ReceiverSignature) = value.DecodeEnvelopeReceiverId();
if (string.IsNullOrEmpty(EnvelopeUuid) || string.IsNullOrEmpty(ReceiverSignature))
throw new BadRequestException("Der EnvelopeReceiverKey muss ein gültiger Base64-kodierter String sein, der die EnvelopeUuid und die ReceiverSignature enthält.");
Envelope = new TEnvelopeQuery()
{
Uuid = EnvelopeUuid
};
Receiver = new TReceiverQuery()
{
Signature = ReceiverSignature
};
_key = value;
}
}
/// <summary>
/// Repräsentiert eine Abfrage für Umschläge.
/// </summary>
public virtual TEnvelopeQuery Envelope { get; set; } = new();
/// <summary>
/// Stellt eine Abfrage dar, um die Details eines Empfängers zu lesen.
/// um spezifische Informationen über einen Empfänger abzurufen.
/// </summary>
public virtual TReceiverQuery Receiver { get; set; } = new();
}

View File

@@ -0,0 +1,20 @@
using AutoMapper;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Common.Query;
/// <summary>
///
/// </summary>
public class MappingProfile : Profile
{
/// <summary>
///
/// </summary>
public MappingProfile()
{
CreateMap<EnvelopeQueryBase, Envelope>();
CreateMap<ReceiverQueryBase, Receiver>();
CreateMap<EnvelopeReceiverQueryBase, EnvelopeReceiver>();
}
}

View File

@@ -0,0 +1,23 @@
namespace EnvelopeGenerator.Application.Common.Query;
/// <summary>
/// Stellt eine Abfrage dar, um die Details eines Empfängers zu lesen.
/// um spezifische Informationen über einen Empfänger abzurufen.
/// </summary>
public record ReceiverQueryBase
{
/// <summary>
/// ID des Empfängers
/// </summary>
public virtual int? Id { get; set; }
/// <summary>
/// E-Mail Adresse des Empfängers
/// </summary>
public virtual string? EmailAddress { get; set; }
/// <summary>
/// Eindeutige Signatur des Empfängers
/// </summary>
public virtual string? Signature { get; set; }
}

View File

@@ -1,14 +1,14 @@
using Dapper;
using EnvelopeGenerator.Application.Contracts.SQLExecutor;
using EnvelopeGenerator.Application.Exceptions;
using EnvelopeGenerator.Application.Interfaces.SQLExecutor;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.SQL;
namespace EnvelopeGenerator.Application.Common.SQL;
/// <summary>
///
/// </summary>
public class DocumentCreateReadSQL : ISQL<EnvelopeDocument>
public class DocumentCreateReadSQL : ISQL<Document>
{
/// <summary>
/// Base64, OUT_UID

View File

@@ -1,9 +1,9 @@
using Dapper;
using EnvelopeGenerator.Application.Contracts.SQLExecutor;
using EnvelopeGenerator.Application.Interfaces.SQLExecutor;
using EnvelopeGenerator.Domain.Entities;
using System.Data;
namespace EnvelopeGenerator.Application.SQL;
namespace EnvelopeGenerator.Application.Common.SQL;
/// <summary>
///

View File

@@ -1,8 +1,8 @@
using Dapper;
using EnvelopeGenerator.Application.Contracts.SQLExecutor;
using EnvelopeGenerator.Application.Interfaces.SQLExecutor;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.SQL;
namespace EnvelopeGenerator.Application.Common.SQL;
/// <summary>
///

View File

@@ -1,6 +1,6 @@
using System.Globalization;
namespace EnvelopeGenerator.Application.SQL;
namespace EnvelopeGenerator.Application.Common.SQL;
/// <summary>
/// Extension method for converting objects to SQL parameter strings.

View File

@@ -1,12 +0,0 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Contracts.Repositories;
/// <summary>
///
/// </summary>
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public interface IEnvelopeCertificateRepository : ICRUDRepository<EnvelopeCertificate, int>
{
}

View File

@@ -1,13 +0,0 @@
using DigitalData.Core.Abstraction.Application;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Contracts.Services;
/// <summary>
///
/// </summary>
[Obsolete("Use MediatR")]
public interface IEnvelopeCertificateService : IBasicCRUDService<EnvelopeCertificateDto, EnvelopeCertificate, int>
{
}

View File

@@ -1,13 +0,0 @@
using DigitalData.Core.Abstraction.Application;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Contracts.Services;
/// <summary>
///
/// </summary>
[Obsolete("Use MediatR")]
public interface IEnvelopeDocumentService : IBasicCRUDService<EnvelopeDocumentDto, EnvelopeDocument, int>
{
}

View File

@@ -1,50 +0,0 @@
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs;
/// <summary>
/// Data Transfer Object representing certificate information for an envelope.
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public class EnvelopeCertificateDto
{
/// <summary>
/// Gets the unique identifier of the certificate.
/// </summary>
public int Id { get; init; }
/// <summary>
/// Gets the envelope ID associated with the certificate.
/// </summary>
public int EnvelopeId { get; init; }
/// <summary>
/// Gets the UUID of the envelope.
/// </summary>
public string EnvelopeUuid { get; init; } = string.Empty;
/// <summary>
/// Gets the subject of the envelope.
/// </summary>
public string EnvelopeSubject { get; init; } = string.Empty;
/// <summary>
/// Gets the ID of the creator of the envelope.
/// </summary>
public int CreatorId { get; init; }
/// <summary>
/// Gets the name of the creator.
/// </summary>
public string CreatorName { get; init; } = string.Empty;
/// <summary>
/// Gets the email address of the creator.
/// </summary>
public string CreatorEmail { get; init; } = string.Empty;
/// <summary>
/// Gets the current status of the envelope.
/// </summary>
public int EnvelopeStatus { get; init; }
}

View File

@@ -1,22 +0,0 @@
using EnvelopeGenerator.Application.DTOs.Receiver;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.DTOs.EnvelopeReceiver;
/// <summary>
///
/// </summary>
[ApiExplorerSettings(IgnoreApi = true)]
public record EnvelopeReceiverDto() : EnvelopeReceiverBasicDto()
{
/// <summary>
///
/// </summary>
public EnvelopeDto? Envelope { get; set; }
/// <summary>
///
/// </summary>
public ReceiverReadDto? Receiver { get; set; }
}

View File

@@ -1,53 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
using System.Text;
namespace EnvelopeGenerator.Application.DTOs.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;
};

View File

@@ -1,13 +1,12 @@
using EnvelopeGenerator.Application.Configurations;
using DigitalData.Core.Client;
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Application.Interfaces.Services;
using EnvelopeGenerator.Application.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using DigitalData.Core.Client;
using QRCoder;
using EnvelopeGenerator.Application.Contracts.Services;
using System.Reflection;
using EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create;
namespace EnvelopeGenerator.Application;
@@ -33,7 +32,6 @@ public static class DependencyInjection
services.TryAddScoped<IDocumentStatusService, DocumentStatusService>();
services.TryAddScoped<IEmailTemplateService, EmailTemplateService>();
services.TryAddScoped<IEnvelopeService, EnvelopeService>();
services.TryAddScoped<IEnvelopeCertificateService, EnvelopeCertificateService>();
services.TryAddScoped<IEnvelopeDocumentService, EnvelopeDocumentService>();
services.TryAddScoped<IEnvelopeReceiverService, EnvelopeReceiverService>();
services.TryAddScoped<IEnvelopeTypeService, EnvelopeTypeService>();
@@ -59,7 +57,6 @@ public static class DependencyInjection
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
cfg.RegisterServicesFromAssembly(typeof(CreateEnvelopeReceiverCommandHandler).Assembly);
});
return services;

View File

@@ -0,0 +1,12 @@
namespace EnvelopeGenerator.Application.DocStatus.Commands;
/// <summary>
///
/// </summary>
public record CreateDocStatusCommand : ModifyDocStatusCommandBase
{
/// <summary>
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
/// </summary>
public DateTime AddedWhen => StatusChangedWhen;
}

View File

@@ -0,0 +1,54 @@
using EnvelopeGenerator.Application.Common.Query;
using EnvelopeGenerator.Domain.Constants;
namespace EnvelopeGenerator.Application.DocStatus.Commands;
/// <summary>
///
/// </summary>
public record ModifyDocStatusCommandBase : EnvelopeReceiverQueryBase
{
/// <summary>
///
/// </summary>
public int? EnvelopeId => Envelope.Id;
/// <summary>
///
/// </summary>
public int? ReceiverId => Receiver.Id;
/// <summary>
///
/// </summary>
public override ReceiverQueryBase Receiver { get => base.Receiver; set => base.Receiver = value; }
/// <summary>
/// Gets the current status code.
/// </summary>
public DocumentStatus Status => Value is null ? DocumentStatus.Created : DocumentStatus.Signed;
/// <summary>
/// Gets or sets the display value associated with the status.
/// </summary>
public string? Value { get; set; }
/// <summary>
/// Gets timestamp when this record was added.
/// </summary>
public DateTime StatusChangedWhen { get; } = DateTime.Now;
/// <summary>
/// Maps the current command to a new instance of the specified type.
/// </summary>
/// <typeparam name="TDest"></typeparam>
/// <returns></returns>
public TDest To<TDest>() where TDest : ModifyDocStatusCommandBase, new()
=> new()
{
Key = Key,
Envelope = Envelope,
Receiver = Receiver,
Value = Value
};
}

View File

@@ -0,0 +1,77 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Extensions;
namespace EnvelopeGenerator.Application.DocStatus.Commands;
/// <summary>
/// Represents a command to save the status of a document, either by creating a new status or updating an existing one based on the provided envelope and receiver identifiers.
/// It returns the identifier of the saved document status.
/// </summary>
public record SaveDocStatusCommand : ModifyDocStatusCommandBase, IRequest<DocumentStatusDto?>;
/// <summary>
///
/// </summary>
public class SaveDocStatusCommandHandler : IRequestHandler<SaveDocStatusCommand, DocumentStatusDto?>
{
private readonly IMapper _mapper;
private readonly IRepository<DocumentStatus> _repo;
private readonly IRepository<Envelope> _envRepo;
private readonly IRepository<Receiver> _rcvRepo;
/// <summary>
///
/// </summary>
/// <param name="mapper"></param>
/// <param name="repo"></param>
/// <param name="rcvRepo"></param>
/// <param name="envRepo"></param>
public SaveDocStatusCommandHandler(IMapper mapper, IRepository<DocumentStatus> repo, IRepository<Receiver> rcvRepo, IRepository<Envelope> envRepo)
{
_mapper = mapper;
_repo = repo;
_rcvRepo = rcvRepo;
_envRepo = envRepo;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<DocumentStatusDto?> Handle(SaveDocStatusCommand request, CancellationToken cancel)
{
// ceck if exists
bool isExists = await _repo.ReadOnly().Where(request).AnyAsync(cancel);
var env = await _envRepo.ReadOnly().Where(request.Envelope).FirstAsync(cancel);
var rcv = await _rcvRepo.ReadOnly().Where(request.Receiver).FirstAsync(cancel);
request.Envelope.Id = env.Id;
request.Receiver.Id = rcv.Id;
if (isExists)
{
var uReq = request.To<UpdateDocStatusCommand>();
await _repo.UpdateAsync(uReq, q => q.Where(request), cancel);
}
else
{
var cReq = request.To<CreateDocStatusCommand>();
await _repo.CreateAsync(cReq, cancel);
}
var docStatus = await _repo.ReadOnly().Where(request).SingleOrDefaultAsync(cancel);
return _mapper.Map<DocumentStatusDto>(docStatus);
}
}

View File

@@ -0,0 +1,14 @@
using EnvelopeGenerator.Domain;
namespace EnvelopeGenerator.Application.DocStatus.Commands;
/// <summary>
///
/// </summary>
public record UpdateDocStatusCommand : ModifyDocStatusCommandBase
{
/// <summary>
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
/// </summary>
public DateTime? ChangedWhen => StatusChangedWhen;
}

View File

@@ -0,0 +1,25 @@
using AutoMapper;
using EnvelopeGenerator.Application.DocStatus.Commands;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.DocStatus;
/// <summary>
///
/// </summary>
public class MappingProfile : Profile
{
/// <summary>
///
/// </summary>
public MappingProfile()
{
CreateMap<CreateDocStatusCommand, DocumentStatus>()
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
.ForMember(dest => dest.Receiver, opt => opt.Ignore());
CreateMap<UpdateDocStatusCommand, DocumentStatus>()
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
.ForMember(dest => dest.Receiver, opt => opt.Ignore());
}
}

View File

@@ -1,18 +0,0 @@
using AutoMapper;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Documents.Queries.Read;
/// <summary>
///
/// </summary>
public class ReadDocumentMappingProfile : Profile
{
/// <summary>
///
/// </summary>
public ReadDocumentMappingProfile()
{
CreateMap<EnvelopeDocument, ReadDocumentResponse>();
}
}

View File

@@ -1,12 +0,0 @@
using MediatR;
namespace EnvelopeGenerator.Application.Documents.Queries.Read;
/// <summary>
/// Represents a query to read a document based on its unique identifier or associated envelope identifier.
/// </summary>
/// <param name="Id">The unique identifier of the document. Optional.</param>
/// <param name="EnvelopeId">The identifier of the envelope associated with the document. Optional.</param>
public record ReadDocumentQuery(int? Id = null, int? EnvelopeId = null) : IRequest<ReadDocumentResponse?>
{
}

View File

@@ -1,48 +0,0 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
namespace EnvelopeGenerator.Application.Documents.Queries.Read;
/// <summary>
/// Handles queries for reading <see cref="EnvelopeDocument"/> data based on either the document ID or the envelope ID.
/// </summary>
public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, ReadDocumentResponse?>
{
/// <summary>
/// Repository for accessing <see cref="EnvelopeDocument"/> entities.
/// </summary>
private readonly IRepository<EnvelopeDocument> _repo;
/// <summary>
/// Initializes a new instance of the <see cref="ReadDocumentQueryHandler"/> class.
/// </summary>
/// <param name="envelopeDocumentRepository">The repository used to access <see cref="EnvelopeDocument"/> entities.</param>
public ReadDocumentQueryHandler(IRepository<EnvelopeDocument> envelopeDocumentRepository)
{
_repo = envelopeDocumentRepository;
}
/// <summary>
/// Handles the <see cref="ReadDocumentQuery"/> and returns a <see cref="ReadDocumentResponse"/> based on the provided identifiers.
/// </summary>
/// <param name="query">The query containing the document ID or envelope ID to search for.</param>
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
/// <returns>
/// A <see cref="ReadDocumentResponse"/> if a matching document is found; otherwise, <c>null</c>.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown when neither <see cref="ReadDocumentQuery.Id"/> nor <see cref="ReadDocumentQuery.EnvelopeId"/> is provided.
/// </exception>
[Obsolete("Use MediatR")]
public async Task<ReadDocumentResponse?> Handle(ReadDocumentQuery query, CancellationToken cancellationToken)
{
if (query.Id is not null)
return await _repo.ReadOrDefaultAsync<ReadDocumentResponse>(d => d.Id == query.Id);
else if (query.EnvelopeId is not null)
return await _repo.ReadOrDefaultAsync<ReadDocumentResponse>(d => d.EnvelopeId == query.EnvelopeId);
throw new InvalidOperationException(
$"Invalid {nameof(ReadDocumentQuery)}: either {nameof(query.Id)} or {nameof(query.EnvelopeId)} must be provided.");
}
}

View File

@@ -1,12 +0,0 @@
namespace EnvelopeGenerator.Application.Documents.Queries.Read;
/// <summary>
/// Represents the response for reading a document.
/// </summary>
public class ReadDocumentResponse : ReadDocumentResponseBase
{
/// <summary>
/// The binary data of the document, if available.
/// </summary>
public byte[]? ByteData { get; init; }
}

View File

@@ -1,22 +0,0 @@
namespace EnvelopeGenerator.Application.Documents.Queries.Read;
/// <summary>
/// Represents the response for reading a document.
/// </summary>
public class ReadDocumentResponseBase
{
/// <summary>
/// The unique identifier of the document.
/// </summary>
public int Guid { get; init; }
/// <summary>
/// The identifier of the associated envelope.
/// </summary>
public int EnvelopeId { get; init; }
/// <summary>
/// The date and time when the document was added.
/// </summary>
public DateTime AddedWhen { get; init; }
}

View File

@@ -0,0 +1,69 @@
using DigitalData.Core.Abstraction.Application.Repository;
using MediatR;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using EnvelopeGenerator.Application.Common.Dto;
namespace EnvelopeGenerator.Application.Documents.Queries;
/// <summary>
/// Represents a query to read a document based on its unique identifier or associated envelope identifier.
/// </summary>
/// <param name="Id">The unique identifier of the document. Optional.</param>
/// <param name="EnvelopeId">The identifier of the envelope associated with the document. Optional.</param>
public record ReadDocumentQuery(int? Id = null, int? EnvelopeId = null) : IRequest<DocumentDto?>
{
}
/// <summary>
/// Handles queries for reading <see cref="Document"/> data based on either the document ID or the envelope ID.
/// </summary>
public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, DocumentDto?>
{
/// <summary>
/// TempRepo for accessing <see cref="Document"/> entities.
/// </summary>
private readonly IRepository<Document> _repo;
private readonly IMapper _mapper;
/// <summary>
/// Initializes a new instance of the <see cref="ReadDocumentQueryHandler"/> class.
/// </summary>
/// <param name="envelopeDocumentRepository">The repository used to access <see cref="Document"/> entities.</param>
/// <param name="mapper"></param>
public ReadDocumentQueryHandler(IRepository<Document> envelopeDocumentRepository, IMapper mapper)
{
_repo = envelopeDocumentRepository;
_mapper = mapper;
}
/// <summary>
/// Handles the <see cref="ReadDocumentQuery"/> and returns a <see cref="DocumentDto"/> based on the provided identifiers.
/// </summary>
/// <param name="query">The query containing the document ID or envelope ID to search for.</param>
/// <param name="cancel">A token to monitor for cancellation requests.</param>
/// <returns>
/// A <see cref="DocumentDto"/> if a matching document is found; otherwise, <c>null</c>.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown when neither <see cref="ReadDocumentQuery.Id"/> nor <see cref="ReadDocumentQuery.EnvelopeId"/> is provided.
/// </exception>
public async Task<DocumentDto?> Handle(ReadDocumentQuery query, CancellationToken cancel)
{
if (query.Id is not null)
{
var doc = await _repo.ReadOnly().Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
return _mapper.Map<DocumentDto>(doc);
}
else if (query.EnvelopeId is not null)
{
var doc = await _repo.ReadOnly().Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
return _mapper.Map<DocumentDto>(doc);
}
throw new InvalidOperationException(
$"Invalid {nameof(ReadDocumentQuery)}: either {nameof(query.Id)} or {nameof(query.EnvelopeId)} must be provided.");
}
}

View File

@@ -1,4 +1,5 @@
using EnvelopeGenerator.Domain;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset;
@@ -33,8 +34,8 @@ public record ResetEmailTemplateCommand : EmailTemplateQuery, IRequest
///
/// </summary>
/// <param name="Id">Die optionale ID der E-Mail-Vorlage, die zurückgesetzt werden soll.</param>
/// <param name="Type">Der Typ der E-Mail-Vorlage, z. B. <see cref="Constants.EmailTemplateType"/> (optional).</param>
public ResetEmailTemplateCommand(int? Id = null, Constants.EmailTemplateType? Type = null) : base(Id, Type)
/// <param name="Type">Der Typ der E-Mail-Vorlage, z. B. <see cref="EmailTemplateType"/> (optional).</param>
public ResetEmailTemplateCommand(int? Id = null, EmailTemplateType? Type = null) : base(Id, Type)
{
}
};

View File

@@ -1,7 +1,9 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
using System.Linq;
namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset;
@@ -31,10 +33,10 @@ public class ResetEmailTemplateCommandHandler : IRequestHandler<ResetEmailTempla
public async Task Handle(ResetEmailTemplateCommand request, CancellationToken cancel)
{
var temps = request.Id is not null
? await _repository.ReadAllAsync<EmailTemplateDto>(t => t.Id == request.Id, cancel)
? await _repository.ReadOnly().Where(t => t.Id == request.Id).ToListAsync(cancel)
: request.Type is not null
? await _repository.ReadAllAsync<EmailTemplateDto>(t => t.Name == request.Type.ToString(), cancel)
: await _repository.ReadAllAsync<EmailTemplateDto>(cancellation: cancel);
? await _repository.ReadOnly().Where(t => t.Name == request.Type.ToString()).ToListAsync(cancel)
: await _repository.ReadOnly().ToListAsync(cancel);
foreach (var temp in temps)
{
@@ -82,7 +84,7 @@ public class ResetEmailTemplateCommandHandler : IRequestHandler<ResetEmailTempla
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]",
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 Include Rejected gesetzt. <p> \r\nMit freundlichen Grüßen<br />\r\n<br />\r\n[NAME_PORTAL]",
Subject = "'[DOCUMENT_TITLE]' - Unterzeichnungsvorgang zurückgezogen"
},
new(){

View File

@@ -1,9 +1,11 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Application.Exceptions;
using EnvelopeGenerator.Domain;
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Dto;
namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Update;
@@ -13,14 +15,17 @@ namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Update;
public class UpdateEmailTemplateCommandHandler : IRequestHandler<UpdateEmailTemplateCommand>
{
private readonly IRepository<EmailTemplate> _repository;
private readonly IMapper _mapper;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
public UpdateEmailTemplateCommandHandler(IRepository<EmailTemplate> repository)
public UpdateEmailTemplateCommandHandler(IRepository<EmailTemplate> repository, IMapper mapper)
{
_repository = repository;
_mapper = mapper;
}
/// <summary>
@@ -34,32 +39,34 @@ public class UpdateEmailTemplateCommandHandler : IRequestHandler<UpdateEmailTemp
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public async Task Handle(UpdateEmailTemplateCommand request, CancellationToken cancel)
{
EmailTemplateDto? temp;
EmailTemplateDto? tempDto;
if (request.EmailTemplateQuery?.Id is int id)
{
temp = await _repository.ReadOrDefaultAsync<EmailTemplateDto>(t => t.Id == id, single: false, cancel);
var temp = await _repository.ReadOnly().Where(t => t.Id == id).FirstOrDefaultAsync(cancel);
tempDto = _mapper.Map<EmailTemplateDto>(temp);
}
else if (request!.EmailTemplateQuery!.Type is Constants.EmailTemplateType type)
else if (request!.EmailTemplateQuery!.Type is EmailTemplateType type)
{
temp = await _repository.ReadOrDefaultAsync<EmailTemplateDto>(t => t.Name == type.ToString(), single: false, cancel);
var temp = await _repository.ReadOnly().Where(t => t.Name == type.ToString()).FirstOrDefaultAsync(cancel);
tempDto = _mapper.Map<EmailTemplateDto>(temp);
}
else
{
throw new InvalidOperationException("Both id and type is null. Id: " + request.EmailTemplateQuery.Id +". Type: " + request.EmailTemplateQuery.Type.ToString());
}
if (temp == null)
if (tempDto == null)
{
throw new NotFoundException();
}
if (request.Body is not null)
temp.Body = request.Body;
tempDto.Body = request.Body;
if (request.Subject is not null)
temp.Subject = request.Subject;
tempDto.Subject = request.Subject;
await _repository.UpdateAsync(temp, t => t.Id == temp.Id, cancel);
await _repository.UpdateAsync(tempDto, t => t.Id == tempDto.Id, cancel);
}
}

View File

@@ -1,4 +1,4 @@
using EnvelopeGenerator.Domain;
using EnvelopeGenerator.Domain.Constants;
namespace EnvelopeGenerator.Application.EmailTemplates;
@@ -7,7 +7,7 @@ namespace EnvelopeGenerator.Application.EmailTemplates;
/// Die Standardkultur ist "de-DE".
/// </summary>
/// <param name="Id">Die eindeutige Kennung der E-Mail-Vorlage (optional).</param>
/// <param name="Type">Der Typ der E-Mail-Vorlage, z. B. <see cref="Constants.EmailTemplateType"/> (optional). Beispiele:
/// <param name="Type">Der Typ der E-Mail-Vorlage, z. B. <see cref="EmailTemplateType"/> (optional). Beispiele:
/// 0 - DocumentReceived: Benachrichtigung über den Empfang eines Dokuments.
/// 1 - DocumentSigned: Benachrichtigung über die Unterzeichnung eines Dokuments.
/// 2 - DocumentDeleted: Benachrichtigung über das Löschen eines Dokuments.
@@ -19,6 +19,6 @@ namespace EnvelopeGenerator.Application.EmailTemplates;
/// 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 EmailTemplateQuery(int? Id = null, Constants.EmailTemplateType? Type = null)
public record EmailTemplateQuery(int? Id = null, EmailTemplateType? Type = null)
{
}

View File

@@ -1,4 +1,5 @@
using AutoMapper;
using EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
using EnvelopeGenerator.Domain.Entities;
using System;
using System.Collections.Generic;
@@ -6,17 +7,17 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
namespace EnvelopeGenerator.Application.EmailTemplates;
/// <summary>
///
/// </summary>
public class ReadEmailTemplateMappingProfile : Profile
public class MappingProfile : Profile
{
/// <summary>
///
/// </summary>
public ReadEmailTemplateMappingProfile()
public MappingProfile()
{
CreateMap<EmailTemplate, ReadEmailTemplateResponse>();
}

View File

@@ -1,6 +1,6 @@
using AutoMapper;
using EnvelopeGenerator.Application.Contracts.Repositories;
using EnvelopeGenerator.Domain;
using EnvelopeGenerator.Application.Interfaces.Repositories;
using EnvelopeGenerator.Domain.Constants;
using MediatR;
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
@@ -41,7 +41,7 @@ public class ReadEmailTemplateQueryHandler : IRequestHandler<ReadEmailTemplateQu
{
var temp = request.Id is int id
? await _repository.ReadByIdAsync(id)
: request.Type is Constants.EmailTemplateType type
: request.Type is EmailTemplateType type
? await _repository.ReadByNameAsync(type)
: throw new InvalidOperationException("Either a valid integer ID or a valid EmailTemplateType must be provided in the request.");

View File

@@ -1,10 +0,0 @@
namespace EnvelopeGenerator.Application
{
public enum EnvelopeFlag
{
EnvelopeOrReceiverNonexists,
NonDecodableEnvelopeReceiverId,
WrongEnvelopeReceiverId,
AccessCodeNull
}
}

View File

@@ -14,22 +14,34 @@
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.0.0" />
<PackageReference Include="DigitalData.Core.Application" Version="3.3.4" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.2.1" />
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
<PackageReference Include="DigitalData.Core.Client" Version="2.1.0" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
<PackageReference Include="HtmlSanitizer" Version="8.0.865" />
<PackageReference Include="MediatR" Version="12.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.18" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="Otp.NET" Version="1.4.0" />
<PackageReference Include="QRCoder" Version="1.6.0" />
<PackageReference Include="QRCoder-ImageSharp" Version="0.10.0" />
<PackageReference Include="UserManager" Version="1.1.1" />
<PackageReference Include="UserManager" Version="1.1.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Extensions\EnvelopeGenerator.Extensions.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,21 +0,0 @@
using EnvelopeGenerator.Application.Envelopes.Commands;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create;
/// <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>;

View File

@@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create;
#region DTOs
/// <summary>
/// Signaturposition auf einem Dokument.
/// </summary>
/// <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] double X, [Required] double Y, [Required] int Page);
/// <summary>
/// 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)
{
private string _emailAddress = string.Empty;
/// <summary>
/// E-Mail-Adresse des Empfängers.
/// </summary>
[Required]
public string EmailAddress { get => _emailAddress.ToLower(); init => _emailAddress = value.ToLower(); }
};
/// <summary>
/// DTO zum Erstellen eines Dokuments.
/// </summary>
public record DocumentCreateCommand()
{
/// <summary>
/// Die Dokumentdaten im Base64-String-Format. Wird verwendet, wenn das Dokument als Base64-codierter String bereitgestellt wird.
/// </summary>
[Required]
public required string DataAsBase64 { get; init; }
};
#endregion

View File

@@ -1,21 +0,0 @@
using AutoMapper;
using EnvelopeGenerator.Application.DTOs.Receiver;
using EnvelopeGenerator.Application.Envelopes.Commands;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create;
/// <summary>
///
/// </summary>
public class CreateEnvelopeReceiverMappingProfile : Profile
{
/// <summary>
///
/// </summary>
public CreateEnvelopeReceiverMappingProfile()
{
CreateMap<Envelope, CreateEnvelopeResponse>();
CreateMap<Receiver, ReceiverReadDto>();
}
}

View File

@@ -1,39 +0,0 @@
using DigitalData.UserManager.Domain.Entities;
using EnvelopeGenerator.Application.DTOs.Receiver;
using EnvelopeGenerator.Application.Envelopes.Commands;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create;
/// <summary>
///
/// </summary>
public record CreateEnvelopeReceiverResponse : CreateEnvelopeResponse
{
/// <summary>
///
/// </summary>
/// <param name="Id"></param>
/// <param name="UserId"></param>
/// <param name="Status"></param>
/// <param name="Uuid"></param>
/// <param name="Message"></param>
/// <param name="AddedWhen"></param>
/// <param name="ChangedWhen"></param>
/// <param name="Title"></param>
/// <param name="Language"></param>
/// <param name="TFAEnabled"></param>
/// <param name="User"></param>
public CreateEnvelopeReceiverResponse(int Id, int UserId, int Status, string Uuid, string? Message, DateTime AddedWhen, DateTime? ChangedWhen, string? Title, string Language, bool TFAEnabled, User User) : base(Id, UserId, Status, Uuid, Message, AddedWhen, ChangedWhen, Title, Language, TFAEnabled, User)
{
}
/// <summary>
///
/// </summary>
public IEnumerable<ReceiverReadDto> SentReceiver { get; set; } = new List<ReceiverReadDto>();
/// <summary>
///
/// </summary>
public IEnumerable<ReceiverGetOrCreateCommand> UnsentReceivers { get; set; } = new List<ReceiverGetOrCreateCommand>();
}

View File

@@ -0,0 +1,81 @@
using EnvelopeGenerator.Application.Envelopes.Commands;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
/// <summary>
/// Befehl zur Erstellung eines Umschlags.
/// </summary>
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>
/// Signaturposition auf einem Dokument.
/// </summary>
/// <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] double X, [Required] double Y, [Required] int Page);
/// <summary>
/// DTO für Empfänger, die erstellt oder abgerufen werden sollen.
/// Wenn nicht, wird sie erstellt und mit einer Signatur versehen.
/// </summary>
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();
}
}
/// <summary>
/// DTO zum Erstellen eines Dokuments.
/// </summary>
public record DocumentCreateCommand()
{
/// <summary>
/// Die Dokumentdaten im Base64-String-Format. Wird verwendet, wenn das Dokument als Base64-codierter String bereitgestellt wird.
/// </summary>
[Required]
public required string DataAsBase64 { get; init; }
};
#endregion

View File

@@ -1,10 +1,10 @@
using AutoMapper;
using EnvelopeGenerator.Application.Contracts.SQLExecutor;
using EnvelopeGenerator.Application.DTOs.Receiver;
using EnvelopeGenerator.Application.Interfaces.SQLExecutor;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using EnvelopeGenerator.Application.Common.Dto.Receiver;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
/// <summary>
/// Handles the creation of an envelope along with its associated document and recipients.
@@ -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();
@@ -61,7 +59,7 @@ public class CreateEnvelopeReceiverCommandHandler : IRequestHandler<CreateEnvelo
var res = _mapper.Map<CreateEnvelopeReceiverResponse>(envelope);
res.UnsentReceivers = unsentRecipients;
res.SentReceiver = _mapper.Map<IEnumerable<ReceiverReadDto>>(sentRecipients);
res.SentReceiver = _mapper.Map<IEnumerable<ReceiverDto>>(sentRecipients);
return res;
}
}

View File

@@ -0,0 +1,20 @@
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Dto.Receiver;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
/// <summary>
///
/// </summary>
public record CreateEnvelopeReceiverResponse : EnvelopeDto
{
/// <summary>
///
/// </summary>
public IEnumerable<ReceiverDto> SentReceiver { get; set; } = new List<ReceiverDto>();
/// <summary>
///
/// </summary>
public IEnumerable<ReceiverGetOrCreateCommand> UnsentReceivers { get; set; } = new List<ReceiverGetOrCreateCommand>();
}

View File

@@ -0,0 +1,20 @@
using AutoMapper;
using EnvelopeGenerator.Application.Common.Dto.Receiver;
using EnvelopeGenerator.Application.Envelopes.Commands;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.EnvelopeReceivers;
/// <summary>
///
/// </summary>
public class MappingProfile : Profile
{
/// <summary>
///
/// </summary>
public MappingProfile()
{
CreateMap<Receiver, ReceiverDto>();
}
}

View File

@@ -1,30 +0,0 @@
using EnvelopeGenerator.Application.Histories;
using EnvelopeGenerator.Application.Envelopes.Queries.Read;
using EnvelopeGenerator.Application.Receivers.Queries.Read;
using MediatR;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries.Read;
/// <summary>
/// Repräsentiert eine Abfrage zum Lesen eines Envelope-Empfängers.
/// </summary>
/// <remarks>
/// Diese Abfrage kombiniert Informationen über einen Umschlag (<see cref="ReadEnvelopeQuery"/>)
/// und einen Empfänger (<see cref="ReadReceiverQuery"/>), um eine vollständige Antwort
/// (<see cref="ReadEnvelopeReceiverResponse"/>) zu generieren.
/// Die Antwort enthält Details wie den Status, die Zuordnung zwischen Umschlag und Empfänger
/// sowie zusätzliche Metadaten.
/// </remarks>
/// <param name="Status">Umschlag oder Empfängerstatus.</param>
public record ReadEnvelopeReceiverQuery(EnvelopeStatusQuery? Status = null) : EnvelopeReceiverQuery(Status), IRequest<ReadEnvelopeReceiverResponse>
{
/// <summary>
/// Der Umschlag, der mit dem Empfänger verknüpft ist.
/// </summary>
public ReadEnvelopeQuery? Envelope { get; init; }
/// <summary>
/// Der Empfänger, der mit dem Umschlag verknüpft ist.
/// </summary>
public ReadReceiverQuery? Receiver { get; init; }
};

View File

@@ -1,94 +0,0 @@
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
using EnvelopeGenerator.Application.Envelopes.Queries.Read;
using EnvelopeGenerator.Application.Receivers.Queries.Read;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries.Read;
/// <summary>
/// Repräsentiert die Antwort für das Lesen eines Envelope-Empfängers.
/// </summary>
/// <remarks>
/// Diese Klasse enthält Informationen über einen spezifischen Empfänger eines Umschlags (Envelope).
/// Sie verknüpft die Empfängerinformationen mit den zugehörigen Umschlagsdaten und bietet zusätzliche Metadaten.
/// </remarks>
/// <param name="UserId">Die eindeutige Kennung des Benutzers, der den Empfänger erstellt hat.</param>
/// <param name="Status">Der Status des Empfängers als numerischer Wert.</param>
public record ReadEnvelopeReceiverResponse(int UserId, int Status)
{
/// <summary>
/// Gibt die zusammengesetzte Kennung des Empfängers zurück, bestehend aus der Umschlags-ID und der Empfänger-ID.
/// </summary>
/// <remarks>
/// Diese Eigenschaft kombiniert die eindeutige Kennung des Umschlags (EnvelopeId) und die des Empfängers (ReceiverId)
/// zu einer einzigen, leicht zugänglichen Struktur.
/// </remarks>
[NotMapped]
public (int Envelope, int Receiver) Id => (Envelope: EnvelopeId, Receiver: ReceiverId);
/// <summary>
/// Die eindeutige Kennung des zugehörigen Umschlags.
/// </summary>
[Required]
public int EnvelopeId { get; init; }
/// <summary>
/// Die eindeutige Kennung des Empfängers.
/// </summary>
[Required]
public int ReceiverId { get; init; }
/// <summary>
/// Die Reihenfolge des Empfängers innerhalb des Umschlags.
/// </summary>
public int Sequence { get; init; }
/// <summary>
/// Der Name des Empfängers. Kann als Platzhalter verwendet werden.
/// </summary>
[TemplatePlaceholder("[NAME_RECEIVER]")]
public string? Name { get; init; }
/// <summary>
/// Die Berufsbezeichnung des Empfängers.
/// </summary>
public string? JobTitle { get; init; }
/// <summary>
/// Der Firmenname des Empfängers.
/// </summary>
public string? CompanyName { get; init; }
/// <summary>
/// Eine private Nachricht, die mit dem Empfänger verknüpft ist.
/// </summary>
public string? PrivateMessage { get; init; }
/// <summary>
/// Das Datum und die Uhrzeit, wann der Empfänger hinzugefügt wurde.
/// </summary>
public DateTime AddedWhen { get; init; }
/// <summary>
/// Das Datum und die Uhrzeit, wann der Empfänger zuletzt geändert wurde (falls vorhanden).
/// </summary>
public DateTime? ChangedWhen { get; init; }
/// <summary>
/// Gibt an, ob der Empfänger eine Telefonnummer hat.
/// </summary>
public bool HasPhoneNumber { get; init; }
/// <summary>
/// Die zugehörigen Umschlagsdaten.
/// </summary>
[Required]
public required ReadEnvelopeResponse Envelope { get; init; }
/// <summary>
/// Die Liste der Empfängerinformationen.
/// </summary>
[Required]
public IEnumerable<ReadReceiverResponse> Receiver { get; init; } = new List<ReadReceiverResponse>();
}

View File

@@ -0,0 +1,143 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Envelopes.Queries;
using EnvelopeGenerator.Application.Receivers.Queries;
using MediatR;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Query;
using EnvelopeGenerator.Application.Common.Extensions;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
/// <summary>
/// Repräsentiert eine Abfrage zum Lesen eines Envelope-Empfängers.
/// Invalid (0): Ungültiger Include.
/// EnvelopeCreated (1001): Der Umschlag wurde erstellt.
/// EnvelopeSaved (1002): Der Umschlag wurde gespeichert.
/// EnvelopeQueued (1003): Der Umschlag wurde zur Verarbeitung eingeplant.
/// EnvelopeSent (1004): Der Umschlag wurde versendet. (Nicht verwendet)
/// EnvelopePartlySigned (1005): Der Umschlag wurde teilweise unterschrieben.
/// EnvelopeCompletelySigned (1006): Der Umschlag wurde vollständig unterschrieben.
/// EnvelopeReportCreated (1007): Ein Abschlussbericht wurde für den Umschlag erstellt.
/// EnvelopeArchived (1008): Der Umschlag wurde archiviert.
/// EnvelopeDeleted (1009): Der Umschlag wurde gelöscht.
/// AccessCodeRequested (2001): Der Zugriffscode wurde angefordert.
/// AccessCodeCorrect (2002): Der Zugriffscode war korrekt.
/// AccessCodeIncorrect (2003): Der Zugriffscode war falsch.
/// DocumentOpened (2004): Das Dokument wurde geöffnet.
/// DocumentSigned (2005): Ein Dokument wurde unterschrieben.
/// SignatureConfirmed (2006): Die Signatur wurde bestätigt.
/// DocumentRejected (2007): Ein Dokument wurde abgelehnt.
/// EnvelopeShared (2008): Der Umschlag wurde geteilt.
/// EnvelopeViewed (2009): Der Umschlag wurde angesehen.
/// DocumentForwarded (4001): Das Dokument wurde weitergeleitet.
/// MessageInvitationSent (3001): Einladung wurde gesendet (vom Trigger verwendet).
/// MessageAccessCodeSent (3002): Zugriffscode wurde gesendet.
/// MessageConfirmationSent (3003): Bestätigungsnachricht wurde gesendet.
/// MessageDeletionSent (3004): Löschbenachrichtigung wurde gesendet.
/// MessageCompletionSent (3005): Abschlussbenachrichtigung wurde gesendet.
/// </summary>
/// <remarks>
/// Diese Abfrage kombiniert Informationen über einen Umschlag (<see cref="ReadEnvelopeQuery"/>)
/// und einen Empfänger (<see cref="ReadReceiverQuery"/>), um eine vollständige Antwort
/// (<see cref="EnvelopeReceiverDto"/>) zu generieren.
/// Die Antwort enthält Details wie den Include, die Zuordnung zwischen Umschlag und Empfänger
/// sowie zusätzliche Metadaten.
/// </remarks>
public record ReadEnvelopeReceiverQuery : EnvelopeReceiverQueryBase<ReadEnvelopeQuery, ReadReceiverQuery>, IRequest<IEnumerable<EnvelopeReceiverDto>>;
/// <summary>
///
/// </summary>
public static class Extensions
{
/// <summary>
///
/// </summary>
/// <param name="mediator"></param>
/// <param name="key"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string key, CancellationToken cancel = default)
{
var q = new ReadEnvelopeReceiverQuery() { Key = key };
return mediator.Send(q, cancel).Then(envRcvs => envRcvs.FirstOrDefault());
}
/// <summary>
///
/// </summary>
/// <param name="mediator"></param>
/// <param name="uuid"></param>
/// <param name="signature"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string uuid, string signature, CancellationToken cancel = default)
{
var q = new ReadEnvelopeReceiverQuery();
q.Envelope.Uuid = uuid;
q.Receiver.Signature = signature;
return mediator.Send(q, cancel).Then(envRcvs => envRcvs.FirstOrDefault());
}
}
/// <summary>
///
/// </summary>
public class ReadEnvelopeReceiverQueryHandler : IRequestHandler<ReadEnvelopeReceiverQuery, IEnumerable<EnvelopeReceiverDto>>
{
private readonly IRepository<EnvelopeReceiver> _repo;
private readonly IMapper _mapper;
/// <summary>
///
/// </summary>
/// <param name="envelopeReceiver"></param>
/// <param name="mapper"></param>
public ReadEnvelopeReceiverQueryHandler(IRepository<EnvelopeReceiver> envelopeReceiver, IMapper mapper)
{
_repo = envelopeReceiver;
_mapper = mapper;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
/// <exception cref="BadRequestException"></exception>
public async Task<IEnumerable<EnvelopeReceiverDto>> Handle(ReadEnvelopeReceiverQuery request, CancellationToken cancel)
{
var q = _repo.ReadOnly().Where(request, notnull: false);
if (request.Envelope.Status is not null)
{
var status = request.Envelope.Status;
if (status.Min is not null)
q = q.Where(er => er.Envelope!.Status >= status.Min);
if (status.Max is not null)
q = q.Where(er => er.Envelope!.Status <= status.Max);
if (status.Include?.Length > 0)
q = q.Where(er => status.Include.Contains(er.Envelope!.Status));
if (status.Ignore is not null)
q = q.Where(er => !status.Ignore.Contains(er.Envelope!.Status));
}
var envRcvs = await q
.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements)
.Include(er => er.Envelope).ThenInclude(e => e!.Histories)
.Include(er => er.Envelope).ThenInclude(e => e!.User)
.Include(er => er.Receiver)
.ToListAsync(cancel);
return _mapper.Map<List<EnvelopeReceiverDto>>(envRcvs);
}
}

View File

@@ -0,0 +1,73 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Application.Common.Extensions;
using MediatR;
using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Query;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
/// <summary>
///
/// </summary>
public record ReceiverAlreadySignedQuery : EnvelopeReceiverQueryBase, IRequest<bool>;
/// <summary>
///
/// </summary>
public static class ReceiverAlreadySignedQueryExtensions
{
/// <summary>
///
/// </summary>
/// <param name="mediator"></param>
/// <param name="key"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static Task<bool> IsSignedAsync(this IMediator mediator, string key, CancellationToken cancel = default)
=> mediator.Send(new ReceiverAlreadySignedQuery { Key = key }, cancel);
/// <summary>
///
/// </summary>
/// <param name="mediator"></param>
/// <param name="uuid"></param>
/// <param name="signature"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public static Task<bool> IsSignedAsync(this IMediator mediator, string uuid, string signature, CancellationToken cancel = default)
=> mediator.Send(new ReceiverAlreadySignedQuery
{
Envelope = new() { Uuid = uuid },
Receiver = new() { Signature = signature }
}, cancel);
}
/// <summary>
///
/// </summary>
public class ReceiverAlreadySignedQueryHandler : IRequestHandler<ReceiverAlreadySignedQuery, bool>
{
private readonly IRepository<History> _repo;
/// <summary>
///
/// </summary>
/// <param name="repo"></param>
public ReceiverAlreadySignedQueryHandler(IRepository<History> repo)
{
_repo = repo;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<bool> Handle(ReceiverAlreadySignedQuery request, CancellationToken cancel = default)
{
return await _repo.ReadOnly().Where(request).Where(h => h.Status == EnvelopeStatus.DocumentSigned).AnyAsync(cancel);
}
}

View File

@@ -1,6 +1,9 @@
using MediatR;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Interfaces.SQLExecutor;
using MediatR;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Application.Envelopes.Commands;
@@ -8,19 +11,37 @@ 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<CreateEnvelopeResponse?>
public record CreateEnvelopeCommand : IRequest<EnvelopeDto?>
{
/// <summary>
/// Id of receiver
/// Der Titel des Umschlags. Dies ist ein Pflichtfeld.
/// </summary>
[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; }
/// <summary>
/// Determines which component is used for envelope processing.
/// When <c>true</c>, processing is delegated to <see cref="IEnvelopeExecutor"/>;
/// when <c>false</c>, <see cref="IRepository{Envelope}"/> is used instead.
/// Note: <see cref="IRepository{Envelope}"/> should only be used in testing scenarios.
/// </summary>
[JsonIgnore]
[BindNever]
public int? UserId { get; set; }
};
[NotMapped]
public bool UseSQLExecutor { get; set; } = true;
}

View File

@@ -1,26 +1,34 @@
using AutoMapper;
using EnvelopeGenerator.Application.Contracts.SQLExecutor;
using EnvelopeGenerator.Application.Interfaces.SQLExecutor;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Application.Common.Dto;
namespace EnvelopeGenerator.Application.Envelopes.Commands;
/// <summary>
///
/// </summary>
public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeCommand, CreateEnvelopeResponse?>
public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeCommand, EnvelopeDto?>
{
private readonly IEnvelopeExecutor _envelopeExecutor;
private readonly IServiceProvider _provider;
private IEnvelopeExecutor Executor => _provider.GetRequiredService<IEnvelopeExecutor>();
private IRepository<Envelope> Repository => _provider.GetRequiredService<IRepository<Envelope>>();
private readonly IMapper _mapper;
/// <summary>
///
/// </summary>
/// <param name="envelopeExecutor"></param>
/// <param name="provider"></param>
/// <param name="mapper"></param>
public CreateEnvelopeCommandHandler(IEnvelopeExecutor envelopeExecutor, IMapper mapper)
public CreateEnvelopeCommandHandler(IServiceProvider provider, IMapper mapper)
{
_envelopeExecutor = envelopeExecutor;
_provider = provider;
_mapper = mapper;
}
@@ -28,14 +36,14 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<CreateEnvelopeResponse?> Handle(CreateEnvelopeCommand request, CancellationToken cancellationToken)
public async Task<EnvelopeDto?> Handle(CreateEnvelopeCommand request, CancellationToken cancel)
{
int userId = request.UserId ?? throw new InvalidOperationException("UserId cannot be null when creating an envelope.");
var envelope = request.UseSQLExecutor
? await Executor.CreateEnvelopeAsync(request.UserId, request.Title, request.Message, request.TFAEnabled, cancel)
: await Repository.CreateAsync(request, cancel);
var envelope = await _envelopeExecutor.CreateEnvelopeAsync(userId, request.Title, request.Message, request.TFAEnabled, cancellationToken);
return _mapper.Map<CreateEnvelopeResponse>(envelope);
return _mapper.Map<EnvelopeDto>(envelope);
}
}
}

View File

@@ -1,19 +0,0 @@
using AutoMapper;
using EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Envelopes.Commands;
/// <summary>
///
/// </summary>
public class CreateEnvelopeMappingProfile : Profile
{
/// <summary>
///
/// </summary>
public CreateEnvelopeMappingProfile()
{
CreateMap<Envelope, CreateEnvelopeReceiverResponse>();
}
}

View File

@@ -1,20 +0,0 @@
using EnvelopeGenerator.Application.Envelopes.Queries.Read;
namespace EnvelopeGenerator.Application.Envelopes.Commands;
/// <summary>
///
/// </summary>
/// <param name="Id"><inheritdoc/></param>
/// <param name="UserId"><inheritdoc/></param>
/// <param name="Status"><inheritdoc/></param>
/// <param name="Uuid"><inheritdoc/></param>
/// <param name="Message"><inheritdoc/></param>
/// <param name="AddedWhen"><inheritdoc/></param>
/// <param name="ChangedWhen"><inheritdoc/></param>
/// <param name="Title"><inheritdoc/></param>
/// <param name="Language"><inheritdoc/></param>
/// <param name="TFAEnabled"><inheritdoc/></param>
/// <param name="User"><inheritdoc/></param>
public record CreateEnvelopeResponse(int Id, int UserId, int Status, string Uuid, string? Message, DateTime AddedWhen, DateTime? ChangedWhen, string? Title, string Language, bool TFAEnabled, DigitalData.UserManager.Domain.Entities.User User)
: ReadEnvelopeResponse(Id, UserId, Status, Uuid, Message, AddedWhen, ChangedWhen, Title, Language, TFAEnabled, User);

View File

@@ -1,16 +0,0 @@
using MediatR;
namespace EnvelopeGenerator.Application.Envelopes;
/// <summary>
/// Repräsentiert eine Abfrage für Umschläge.
/// </summary>
/// <param name="Id">Die eindeutige Kennung des Umschlags.</param>
/// <param name="Status">Der Status des Umschlags.</param>
/// <param name="Uuid">Die universell eindeutige Kennung des Umschlags.</param>
public record EnvelopeQuery(
int? Id = null,
int? Status = null,
string? Uuid = null) : IRequest
{
};

View File

@@ -0,0 +1,22 @@
using AutoMapper;
using EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
using EnvelopeGenerator.Application.Envelopes.Commands;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Envelopes;
/// <summary>
///
/// </summary>
public class MappingProfile : Profile
{
/// <summary>
///
/// </summary>
public MappingProfile()
{
CreateMap<Envelope, CreateEnvelopeReceiverResponse>();
CreateMap<CreateEnvelopeCommand, Envelope>()
.ForMember(dest => dest.Uuid, opt => opt.MapFrom(_ => Guid.NewGuid().ToString()));
}
}

View File

@@ -1,8 +0,0 @@
namespace EnvelopeGenerator.Application.Envelopes.Queries.Read;
/// <summary>
/// Stellt eine Abfrage zum Lesen von Briefumschlägen dar.
/// </summary>
public record ReadEnvelopeQuery : EnvelopeQuery
{
}

View File

@@ -1,36 +0,0 @@
using EnvelopeGenerator.Domain;
namespace EnvelopeGenerator.Application.Envelopes.Queries.Read;
/// <summary>
/// Repräsentiert die Antwort für das Lesen eines Umschlags.
/// </summary>
/// <param name="Id">Die eindeutige Kennung des Umschlags.</param>
/// <param name="UserId">Die Kennung des Benutzers, der den Umschlag erstellt hat.</param>
/// <param name="Status">Der Status des Umschlags als numerischer Wert.</param>
/// <param name="Uuid">Die universelle eindeutige Kennung (UUID) des Umschlags.</param>
/// <param name="Message">Eine optionale Nachricht, die mit dem Umschlag verknüpft ist.</param>
/// <param name="AddedWhen">Das Datum und die Uhrzeit, wann der Umschlag hinzugefügt wurde.</param>
/// <param name="ChangedWhen">Das Datum und die Uhrzeit, wann der Umschlag zuletzt geändert wurde (falls vorhanden).</param>
/// <param name="Title">Ein optionaler Titel des Umschlags.</param>
/// <param name="Language">Die Sprache, die mit dem Umschlag verknüpft ist.</param>
/// <param name="TFAEnabled">Gibt an, ob die Zwei-Faktor-Authentifizierung (TFA) aktiviert ist.</param>
/// <param name="User">Das Benutzerobjekt, das mit dem Umschlag verknüpft ist.</param>
public record ReadEnvelopeResponse(
int Id,
int UserId,
int Status,
string Uuid,
string? Message,
DateTime AddedWhen,
DateTime? ChangedWhen,
string? Title,
string Language,
bool TFAEnabled,
DigitalData.UserManager.Domain.Entities.User User)
{
/// <summary>
/// Gibt den Namen des Status zurück, der dem numerischen Statuswert entspricht.
/// </summary>
public string StatusName => ((Constants.EnvelopeStatus)Status).ToString();
}

View File

@@ -1,16 +1,23 @@
using EnvelopeGenerator.Application.Histories;
using MediatR;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Application.Common.Query;
namespace EnvelopeGenerator.Application.EnvelopeReceivers;
namespace EnvelopeGenerator.Application.Envelopes.Queries;
/// <summary>
/// Stellt eine Abfrage für einen Envelope-Empfänger dar.
/// Repräsentiert eine Abfrage für Umschläge.
/// </summary>
/// <param name="Status">Der Status der Abfrage, optional.</param>
public record EnvelopeReceiverQuery(EnvelopeStatusQuery? Status = null);
public record ReadEnvelopeQuery : EnvelopeQueryBase, IRequest
{
/// <summary>
/// Abfrage des Include des Umschlags
/// </summary>
public EnvelopeStatusQuery? Status { get; init; }
}
/// <summary>
/// Repräsentiert den Status eines Umschlags und dessen Beziehung zum Empfänger. (vgl. auch <see cref="Common.Constants.EnvelopeStatus"/>
/// Invalid (0): Ungültiger Status.
/// Repräsentiert den Include eines Umschlags und dessen Beziehung zum Empfänger. (vgl. auch <see cref="EnvelopeStatusQuery"/>
/// Invalid (0): Ungültiger Include.
/// EnvelopeCreated (1001): Der Umschlag wurde erstellt.
/// EnvelopeSaved (1002): Der Umschlag wurde gespeichert.
/// EnvelopeQueued (1003): Der Umschlag wurde zur Verarbeitung eingeplant.
@@ -35,13 +42,27 @@ public record EnvelopeReceiverQuery(EnvelopeStatusQuery? Status = null);
/// MessageConfirmationSent (3003): Bestätigungsnachricht wurde gesendet.
/// MessageDeletionSent (3004): Löschbenachrichtigung wurde gesendet.
/// MessageCompletionSent (3005): Abschlussbenachrichtigung wurde gesendet.
/// <param name="Min">Der minimale Statuswert, der berücksichtigt werden soll.</param>
/// <param name="Max">Der maximale Statuswert, der berücksichtigt werden soll.</param>
/// <param name="Ignore">Eine Liste von Statuswerten, die ignoriert werden sollen.</param>
/// </summary>
public record EnvelopeStatusQuery(
int? Min = null,
int? Max = null,
int[]? Ignore = null)
public record EnvelopeStatusQuery
{
}
/// <summary>
/// Der minimale Statuswert, der berücksichtigt werden.
/// </summary>
public EnvelopeStatus? Min { get; init; }
/// <summary>
/// Der maximale Statuswert, der berücksichtigt werden.
/// </summary>
public EnvelopeStatus? Max { get; init; }
/// <summary>
/// Eine Liste von Statuswerten, die einbezogen werden.
/// </summary>
public EnvelopeStatus[]? Include { get; init; }
/// <summary>
/// Eine Liste von Statuswerten, die ignoriert werden werden.
/// </summary>
public EnvelopeStatus[]? Ignore { get; init; }
}

View File

@@ -1,6 +1,6 @@
using EnvelopeGenerator.Application.Receivers.Queries.Read;
using EnvelopeGenerator.Application.Receivers.Queries;
namespace EnvelopeGenerator.Application.Envelopes.Queries.ReceiverName;
namespace EnvelopeGenerator.Application.Envelopes.Queries;
/// <summary>
/// Eine Abfrage, um die zuletzt verwendete Anrede eines Empfängers zu ermitteln,

View File

@@ -1,22 +0,0 @@
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
/// Represents an exception that is thrown when a bad request is encountered.
/// </summary>
public class BadRequestException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="BadRequestException"/> class.
/// </summary>
public BadRequestException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BadRequestException"/> class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public BadRequestException(string? message) : base(message)
{
}
}

View File

@@ -1,22 +0,0 @@
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
/// Represents an exception that is thrown when a requested resource is not found.
/// </summary>
public class NotFoundException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="NotFoundException"/> class.
/// </summary>
public NotFoundException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="NotFoundException"/> class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public NotFoundException(string? message) : base(message)
{
}
}

View File

@@ -0,0 +1,108 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using EnvelopeGenerator.Application.Common.Dto.History;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Query;
using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.Histories.Commands;
/// <summary>
///
/// </summary>
public record CreateHistoryCommand : EnvelopeReceiverQueryBase, IRequest<HistoryDto?>
{
/// <summary>
///
/// </summary>
public int? EnvelopeId { get; set; }
/// <summary>
///
/// </summary>
public string? UserReference { get; set; }
/// <summary>
///
/// </summary>
public EnvelopeStatus Status { get; set; }
/// <summary>
///
/// </summary>
public DateTime AddedWhen { get; } = DateTime.Now;
/// <summary>
///
/// </summary>
public DateTime ActionDate => AddedWhen;
/// <summary>
///
/// </summary>
public string? Comment { get; set; }
}
/// <summary>
///
/// </summary>
public class CreateHistoryCommandHandler : IRequestHandler<CreateHistoryCommand, HistoryDto?>
{
private readonly IRepository<History> _repo;
private readonly IRepository<EnvelopeReceiver> _erRepo;
private readonly IMapper _mapper;
/// <summary>
///
/// </summary>
/// <param name="repo"></param>
/// <param name="erRepo"></param>
/// <param name="mapper"></param>
public CreateHistoryCommandHandler(IRepository<History> repo, IRepository<EnvelopeReceiver> erRepo, IMapper mapper)
{
_repo = repo;
_erRepo = erRepo;
_mapper = mapper;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancel"></param>
/// <returns></returns>
public async Task<HistoryDto?> Handle(CreateHistoryCommand request, CancellationToken cancel)
{
if(request.UserReference is null)
{
var receivers = await _erRepo
.ReadOnly()
.Where(request)
.Include(er => er.Receiver)
.ToListAsync(cancel);
if (receivers.Count != 1)
throw new BadRequestException(
receivers.Count > 1
? "Multiple receivers found for the given envelope and receiver criteria."
: "No receiver found for the given envelope and receiver criteria."
);
var receiver = receivers.Single().Receiver
?? throw new BadRequestException("No receiver found for the given envelope and receiver criteria.");
request.UserReference = receiver.EmailAddress;
}
// create entitiy
var hist = await _repo.CreateAsync(request, cancel);
return _mapper.Map<HistoryDto>(hist);
}
}

View File

@@ -0,0 +1,22 @@
using AutoMapper;
using EnvelopeGenerator.Application.Histories.Commands;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.Histories;
/// <summary>
///
/// </summary>
public class MappingProfile: Profile
{
/// <summary>
///
/// </summary>
public MappingProfile()
{
CreateMap<CreateHistoryCommand, History>()
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
.ForMember(dest => dest.Sender, opt => opt.Ignore())
.ForMember(dest => dest.Receiver, opt => opt.Ignore());
}
}

Some files were not shown because too many files have changed in this diff Show More