Compare commits
8 Commits
feat/serve
...
feat/locat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46f1633f79 | ||
|
|
1af07f0df8 | ||
|
|
55c9dfb9c2 | ||
|
|
1a230306a3 | ||
|
|
28022bc669 | ||
|
|
ad5843b7c9 | ||
|
|
173b691e56 | ||
|
|
8a0fe70a88 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -363,10 +363,3 @@ MigrationBackup/
|
|||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
/EnvelopeGenerator.Web/.config/dotnet-tools.json
|
/EnvelopeGenerator.Web/.config/dotnet-tools.json
|
||||||
/EnvelopeGenerator.GeneratorAPI/ClientApp/envelope-generator-ui/.vscode
|
/EnvelopeGenerator.GeneratorAPI/ClientApp/envelope-generator-ui/.vscode
|
||||||
/EnvelopeGenerator.Tests.Application/Services/BugFixTests.cs
|
|
||||||
/EnvelopeGenerator.Tests.Application/annotations.json
|
|
||||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/TekH - SoftHSM Test.md
|
|
||||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/tekh_softHSM_test.md
|
|
||||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/publish-output
|
|
||||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/tekh_softHSM_test.md
|
|
||||||
/ignore/EnvelopeGenerator.Form
|
|
||||||
|
|||||||
324
AGENTS.md
324
AGENTS.md
@@ -1,324 +0,0 @@
|
|||||||
# EnvelopeGenerator - Agent Guide
|
|
||||||
|
|
||||||
## Must Read First
|
|
||||||
- **`COPILOT_CONTEXT.md`** - Architecture, coordinate systems, migration status
|
|
||||||
- **`FORM_APPLICATION_CONTEXT.md`** - Legacy VB.NET features to migrate
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Documentation Catalog
|
|
||||||
|
|
||||||
This section explains every markdown file in the repo: what it contains, why it exists, and when you need it. Read this before deciding which file to open.
|
|
||||||
|
|
||||||
### Core Architecture Docs (Active — Always Relevant)
|
|
||||||
|
|
||||||
#### `AGENTS.md` ← you are here
|
|
||||||
**What:** Authoritative agent/copilot onboarding guide. Covers project structure, route table, render mode rules, coordinate system, API gaps, and common mistakes.
|
|
||||||
**Why it exists:** Single source of truth for agents so they don't re-derive architecture from code on every session.
|
|
||||||
**When you need it:** Always. Read first on every session.
|
|
||||||
**Maintenance:** Update whenever project structure, routes, render modes, or naming conventions change.
|
|
||||||
|
|
||||||
#### `COPILOT_CONTEXT.md`
|
|
||||||
**What:** Deep-dive architecture reference. Covers the full Blazor Auto hybrid setup, authentication flows (JWT + cookies, per-envelope receiver tokens), service registration patterns, coordinate system proof, and an explicit "prefer Server/Server.Client over old WebUI/ReceiverUI" rule.
|
|
||||||
**Why it exists:** Prevents agents from re-deriving or misinterpreting the hybrid SSR+WASM architecture. Also documents coordinate system evidence to prevent regressions (inches vs. points vs. pixels).
|
|
||||||
**When you need it:**
|
|
||||||
- Implementing any new page or service
|
|
||||||
- Debugging auth/cookie issues
|
|
||||||
- Any coordinate conversion question
|
|
||||||
- Unsure which project a new file should go in
|
|
||||||
**Maintenance:** Update when render mode assignments change, new auth schemes are added, or the coordinate system is touched.
|
|
||||||
|
|
||||||
#### `FORM_APPLICATION_CONTEXT.md`
|
|
||||||
**What:** Comprehensive reverse-engineering of the legacy VB.NET WinForms app (`EnvelopeGenerator.Form`). Documents every form (frmMain, frmEnvelopeEditor, frmFieldEditor, etc.), their UI layout, toolbar actions, data models, coordinate system, and a feature-by-feature migration mapping table to `EnvelopeGenerator.Server`.
|
|
||||||
**Why it exists:** The WinForms app is the functional specification for the sender-side web UI. It documents the full intended workflow that is being migrated to Blazor — especially the envelope creation, signature field placement, and dashboard features that are not yet implemented.
|
|
||||||
**When you need it:**
|
|
||||||
- Implementing any sender-side feature (`/sender`, `/sender/editor`, `/sender/envelope/{id}`)
|
|
||||||
- Understanding what a specific toolbar button or form should do
|
|
||||||
- Designing API endpoints for sender workflow
|
|
||||||
- Checking coordinate system behavior (INCHES proof is here)
|
|
||||||
**Maintenance:** Update the mapping table as features are implemented (change `❌ Not implemented` → `✅ Exists`). Do not rename `ReceiverUI` back — it was the old project name; current target is `EnvelopeGenerator.Server`.
|
|
||||||
|
|
||||||
#### `RECEIVER_PDF_VIEWER_CONTEXT.md`
|
|
||||||
**What:** Detailed technical reference for the receiver-side PDF viewer (`EnvelopeReceiverPage.razor`). Documents the PDF.js integration, signature button rendering, JS interop API (`window.pdfViewer`), signature capture flow, HiDPI/zoom/thumbnail config, and the full component lifecycle.
|
|
||||||
**Why it exists:** The receiver PDF viewer is the most complex component in the codebase. This doc prevents agents from misunderstanding the JS↔Blazor boundary, re-implementing already-solved problems (HiDPI, zoom, signature caching), or breaking the signing flow.
|
|
||||||
**When you need it:**
|
|
||||||
- Working on `EnvelopeReceiverPage.razor` or `pdf-viewer.js`
|
|
||||||
- Adding new signature field rendering logic
|
|
||||||
- Debugging signature rendering, zoom, or thumbnail issues
|
|
||||||
- Implementing sender-side PDF.js overlay (signature field placement) — the receiver side is the reference implementation
|
|
||||||
**Maintenance:** Update when `pdf-viewer.js` API changes, new JS interop functions are added, or the signing flow is modified.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Deployment Docs
|
|
||||||
|
|
||||||
#### `EnvelopeGenerator.Server/EnvelopeGenerator.Server/README.md`
|
|
||||||
**What:** IIS publish and deployment guide for `EnvelopeGenerator.Server`. Covers why self-contained publish is required (WASM assembly version strictness), the exact `dotnet publish` command, IIS Application Pool settings (`No Managed Code`), directory structure after publish, and common deployment errors.
|
|
||||||
**Why it exists:** Blazor Auto (Server + WASM hybrid) has non-obvious deployment requirements that differ from a standard ASP.NET Core app. Forgetting self-contained publish or using the wrong app pool will break WASM loading in production.
|
|
||||||
**When you need it:**
|
|
||||||
- Deploying to IIS for the first time
|
|
||||||
- Troubleshooting 500 errors on production after deploy
|
|
||||||
- Setting up a new deployment environment
|
|
||||||
**Maintenance:** Update if the publish target, IIS config, or runtime version changes.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Active Architecture (Post-Migration)
|
|
||||||
|
|
||||||
**Frontend:** Blazor Auto (Server+WASM hybrid)
|
|
||||||
- **EnvelopeGenerator.Server** (Server): `@rendermode InteractiveServer` - PDF viewers requiring DevExpress backend
|
|
||||||
- **EnvelopeGenerator.Server.Client** (WASM): `@rendermode InteractiveWebAssembly` - Login, dashboards, business logic
|
|
||||||
|
|
||||||
**Backend:** EnvelopeGenerator.API (ASP.NET Core 8.0)
|
|
||||||
|
|
||||||
**Proxy:** YARP in EnvelopeGenerator.Server routes `/api/*` → `localhost:8088` (API)
|
|
||||||
|
|
||||||
### Deprecated Projects - DO NOT USE
|
|
||||||
- `EnvelopeGenerator.ReceiverUI` - Pure WASM (migrated to Server)
|
|
||||||
- `EnvelopeGenerator.Web` - Razor Pages (replaced by Server)
|
|
||||||
- **VB.NET projects** (`Form`, `Service`, `BBTests`) - Legacy, read-only for reference
|
|
||||||
|
|
||||||
## Development Commands
|
|
||||||
|
|
||||||
### Run Both Projects (Required)
|
|
||||||
```powershell
|
|
||||||
# Terminal 1 - API Backend
|
|
||||||
cd EnvelopeGenerator.API
|
|
||||||
dotnet run
|
|
||||||
|
|
||||||
# Terminal 2 - Blazor Frontend
|
|
||||||
cd EnvelopeGenerator.Server\EnvelopeGenerator.Server
|
|
||||||
dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
**Critical:** Both must run simultaneously. EnvelopeGenerator.Server proxy forwards `/api/*` to API.
|
|
||||||
|
|
||||||
### Build
|
|
||||||
```powershell
|
|
||||||
dotnet build EnvelopeGenerator.sln
|
|
||||||
```
|
|
||||||
|
|
||||||
## Project Boundaries
|
|
||||||
|
|
||||||
```
|
|
||||||
EnvelopeGenerator.Domain/ # Entities (Envelope, Receiver, Document, etc.)
|
|
||||||
EnvelopeGenerator.Application/ # MediatR CQRS (Commands, Queries, Handlers)
|
|
||||||
EnvelopeGenerator.Infrastructure/ # EF Core, SQL executors, repositories
|
|
||||||
EnvelopeGenerator.API/ # Controllers, endpoints
|
|
||||||
EnvelopeGenerator.Server/
|
|
||||||
EnvelopeGenerator.Server/ # Server-side Blazor components
|
|
||||||
├─ Components/Pages/ # @rendermode InteractiveServer
|
|
||||||
EnvelopeGenerator.Server.Client/ # Client-side WASM components
|
|
||||||
├─ Pages/ # @rendermode InteractiveWebAssembly
|
|
||||||
├─ Services/ # HTTP API clients
|
|
||||||
├─ Models/ # DTOs
|
|
||||||
```
|
|
||||||
|
|
||||||
## Route Structure (Critical)
|
|
||||||
|
|
||||||
| Route | File Location | Render Mode | Purpose |
|
|
||||||
|-------|--------------|-------------|---------|
|
|
||||||
| `/` | `Server.Client/Pages/Index.razor` | WASM | Landing page |
|
|
||||||
| `/sender/login` | `Server.Client/Pages/LoginSenderPage.razor` | WASM | Sender auth |
|
|
||||||
| `/sender` | `Server/Components/Pages/EnvelopeSenderPage.razor` | **Server** | Sender dashboard |
|
|
||||||
| `/sender/editor` | `Server/Components/Pages/EnvelopeSenderEditorPage.razor` | **Server** | Envelope editor + PDF viewer |
|
|
||||||
| `/envelope/login/{key}` | `Server.Client/Pages/LoginReceiverPage.razor` | WASM | Receiver auth |
|
|
||||||
| `/envelope/{key}` | `Server/Components/Pages/EnvelopeReceiverPage.razor` | **Server** | PDF viewer + signing |
|
|
||||||
|
|
||||||
**Rule:** PDF viewers MUST use `@rendermode InteractiveServer` (DevExpress backend requirement). Everything else uses WASM.
|
|
||||||
|
|
||||||
## Coordinate System (CRITICAL)
|
|
||||||
|
|
||||||
**Database stores INCHES** (GdPicture14 native). Origin: top-left, Y-axis down.
|
|
||||||
|
|
||||||
### Conversions
|
|
||||||
```csharp
|
|
||||||
// Database (INCHES) → PDF Points
|
|
||||||
float points = inches * 72;
|
|
||||||
|
|
||||||
// Database (INCHES) → DevExpress DX
|
|
||||||
float dx = inches * 100;
|
|
||||||
|
|
||||||
// PDF.js Pixels → Database (INCHES)
|
|
||||||
float inches = (pixelX / canvasWidth) * pageWidthInches;
|
|
||||||
```
|
|
||||||
|
|
||||||
**A4 Page:** 8.27" wide × 11.69" tall = 595pt × 842pt
|
|
||||||
|
|
||||||
**Signature Field Size:** 1.77" × 1.96" (FIXED, do not change)
|
|
||||||
|
|
||||||
**Evidence:** See `COPILOT_CONTEXT.md` lines 158-185, `EnvelopeGenerator.Form/frmFieldEditor.vb`
|
|
||||||
|
|
||||||
## API Architecture Quirks
|
|
||||||
|
|
||||||
### Monolithic Endpoint (Avoid for UI)
|
|
||||||
`POST /api/EnvelopeReceiver` - Creates envelope+document+receivers+fields atomically.
|
|
||||||
- **Use case:** External API consumers
|
|
||||||
- **Not suitable for:** Step-by-step UI workflow (no draft support, no partial updates)
|
|
||||||
|
|
||||||
### Missing Granular Endpoints (Need to Create)
|
|
||||||
```
|
|
||||||
POST /api/Envelope/draft # Create draft envelope
|
|
||||||
PUT /api/Envelope/{id} # Update metadata
|
|
||||||
DELETE /api/Envelope/{id} # Delete with reason
|
|
||||||
POST /api/Envelope/{id}/document # Upload PDF
|
|
||||||
POST /api/Envelope/{id}/receivers # Add receiver
|
|
||||||
POST /api/Envelope/{id}/signature-fields # Place signature field
|
|
||||||
POST /api/Envelope/{id}/send # Send to receivers
|
|
||||||
```
|
|
||||||
|
|
||||||
See `FORM_APPLICATION_CONTEXT.md` for detailed workflow requirements.
|
|
||||||
|
|
||||||
## Status Color Coding
|
|
||||||
|
|
||||||
Form app uses DevExpress `CustomDrawCell`. EnvelopeGenerator.Server needs CSS:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.envelope-row.status-partly-signed { background-color: #81C784; } /* GREEN_300 */
|
|
||||||
.envelope-row.status-queued,
|
|
||||||
.envelope-row.status-sent { background-color: #FFB74D; } /* ORANGE_300 */
|
|
||||||
.envelope-row.status-completed { background-color: #81C784; }
|
|
||||||
.envelope-row.status-deleted,
|
|
||||||
.envelope-row.status-rejected { background-color: #E57373; } /* RED_300 */
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### YARP Proxy (`EnvelopeGenerator.Server/yarp.json`)
|
|
||||||
Routes `/api/*`, `/swagger/*`, `/openapi/*`, `/scalar/*` → `https://localhost:8088`
|
|
||||||
|
|
||||||
### PDF.js Settings (`EnvelopeGenerator.Server/wwwroot/appsettings.json`)
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"PdfViewerOptions": {
|
|
||||||
"ThumbnailBaseScale": 0.75,
|
|
||||||
"ThumbnailEnableHiDPI": true,
|
|
||||||
"MainCanvasEnableHiDPI": true,
|
|
||||||
"ZoomStepPercentage": 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### API Config (`API/appsettings.json`)
|
|
||||||
- `ConnectionStrings:Default` - SQL Server DB
|
|
||||||
- `AllowedOrigins` - CORS (includes `http://localhost:5131`, `http://localhost:7192`)
|
|
||||||
- `Cache:SignatureCacheExpiration` - Signature persistence timeout
|
|
||||||
- `PSPDFKitLicenseKey` - **DEPRECATED** (use PDF.js instead)
|
|
||||||
|
|
||||||
## Migration Status
|
|
||||||
|
|
||||||
### Complete ✅
|
|
||||||
- Receiver login/authentication
|
|
||||||
- PDF viewing with PDF.js (HiDPI, zoom, thumbnails)
|
|
||||||
- Signature capture (draw/type/image)
|
|
||||||
- Signature caching (Redis/SQL)
|
|
||||||
- Sender login
|
|
||||||
|
|
||||||
### Missing (High Priority) ❌
|
|
||||||
- Sender dashboard (`/sender`) - Empty stub
|
|
||||||
- Envelope editor (`/sender/envelope/{id}`)
|
|
||||||
- Signature field placement tool (PDF.js + draggable overlays)
|
|
||||||
- Granular API endpoints (draft, receivers, fields)
|
|
||||||
- Master-detail grids for receivers/history
|
|
||||||
|
|
||||||
## Common Mistakes (DO NOT REPEAT)
|
|
||||||
|
|
||||||
| Mistake | Why Wrong |
|
|
||||||
|---------|-----------|
|
|
||||||
| Using iText7 in receiver pages | GPL license issue. Use PDF.js overlays. |
|
|
||||||
| Using PSPDFKit | Removed from architecture. Use PDF.js + DevExpress. |
|
|
||||||
| `@rendermode InteractiveWebAssembly` on PDF viewers | DevExpress DxPdfViewer requires server-side rendering. |
|
|
||||||
| Hardcoded quality in PDF.js | Use `appsettings.json` `PdfViewerOptions`. |
|
|
||||||
| Coordinates in points/pixels for DB | Database uses INCHES. Convert before save. |
|
|
||||||
| `BottomMarginBand` for signatures | Repeats on every page. Use `DetailBand`. |
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
**No automated tests exist yet.**
|
|
||||||
|
|
||||||
Manual testing workflow:
|
|
||||||
1. Start API (`dotnet run` in `EnvelopeGenerator.API`)
|
|
||||||
2. Start EnvelopeGenerator.Server (`dotnet run` in `EnvelopeGenerator.Server\EnvelopeGenerator.Server`)
|
|
||||||
3. Navigate to `https://localhost:5131` (or check console output for port)
|
|
||||||
4. Test sender login at `/sender/login`
|
|
||||||
5. Test receiver flow at `/envelope/login/{envelopeKey}`
|
|
||||||
|
|
||||||
## Database
|
|
||||||
|
|
||||||
**SQL Server** (DD_ECM)
|
|
||||||
- Connection string in `API/appsettings.json`
|
|
||||||
- EF Core migrations NOT used (manual SQL scripts)
|
|
||||||
- Stored procedures: `PRSIG_*` prefix
|
|
||||||
|
|
||||||
**Key Tables:**
|
|
||||||
- `TBSIG_ENVELOPE` - Envelope metadata
|
|
||||||
- `TBSIG_ENVELOPE_RECEIVER` - Receiver assignments
|
|
||||||
- `TBSIG_DOC_RECEIVER_ELEMENT` - Signature fields (X, Y in INCHES)
|
|
||||||
- `TBSIG_RECEIVER` - Receiver registry
|
|
||||||
- `TBSIG_DOCUMENT` - PDF binary data
|
|
||||||
- `TBSIG_ENVELOPE_HISTORY` - Audit trail
|
|
||||||
|
|
||||||
## DevExpress
|
|
||||||
|
|
||||||
**License:** Commercial (v25.2.3)
|
|
||||||
**Components Used:**
|
|
||||||
- `DxGrid` - Master-detail grids
|
|
||||||
- `DxPdfViewer` - Server-side PDF rendering
|
|
||||||
- `DxPopup` - Modal dialogs
|
|
||||||
- `DxToolbar` - Action bars
|
|
||||||
- `DxFormLayout` - Forms
|
|
||||||
|
|
||||||
**Theme:** Blazing Berry (default)
|
|
||||||
|
|
||||||
## JavaScript Interop
|
|
||||||
|
|
||||||
**PDF Viewer:** `wwwroot/js/pdf-viewer.js`
|
|
||||||
```javascript
|
|
||||||
window.pdfViewer = {
|
|
||||||
initialize(canvasId, pdfDataUrl, dotNetRef),
|
|
||||||
renderPage(num),
|
|
||||||
renderSignatureButtons(signatures, pageNum, dotNetRef),
|
|
||||||
applySignature(signatureId, dataUrl, fullName, position, place),
|
|
||||||
zoomIn(), zoomOut(), dispose()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Signature Pad:** `wwwroot/js/receiver-signature.js`
|
|
||||||
```javascript
|
|
||||||
window.receiverSignature = {
|
|
||||||
initializeDrawPad(canvasId, dotNetRef),
|
|
||||||
getSignatureDataUrl(canvasId),
|
|
||||||
clearPad(canvasId)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multi-Envelope Support
|
|
||||||
|
|
||||||
Receivers can login to **multiple envelopes simultaneously** via per-envelope cookies:
|
|
||||||
```
|
|
||||||
AuthTokenSignFLOWReceiver.{envelopeKey}
|
|
||||||
```
|
|
||||||
|
|
||||||
Each envelope maintains independent authentication state.
|
|
||||||
|
|
||||||
## External Dependencies
|
|
||||||
|
|
||||||
**CDN:**
|
|
||||||
- PDF.js 3.11.174: `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
|
|
||||||
|
|
||||||
**NuGet (Server.Client):**
|
|
||||||
- `DevExpress.Blazor.*` 25.2.3
|
|
||||||
- `SkiaSharp.*` 3.119.1 (WASM rendering)
|
|
||||||
|
|
||||||
**External Services:**
|
|
||||||
- LDAP/AD authentication (optional)
|
|
||||||
- GTX Messaging (SMS 2FA)
|
|
||||||
- Email dispatcher (signFlow)
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
None required. All config in `appsettings.json`.
|
|
||||||
|
|
||||||
**Local dev ports:**
|
|
||||||
- API: `https://localhost:8088`
|
|
||||||
- WebUI: `https://localhost:5131` or `http://localhost:7192`
|
|
||||||
@@ -1,351 +0,0 @@
|
|||||||
# EnvelopeGenerator — Current Workspace Context
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
Digital document signing system for senders and receivers.
|
|
||||||
|
|
||||||
- Senders authenticate, view envelope lists, and manage envelope workflows.
|
|
||||||
- Receivers authenticate per envelope, open PDFs, create signatures, and apply them in the viewer.
|
|
||||||
- The active UI stack is `Blazor Auto` with server-side and WebAssembly render modes.
|
|
||||||
- Primary UI/PDF libraries are `DevExpress` and `PDF.js`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Active Application Structure
|
|
||||||
|
|
||||||
### Main Host
|
|
||||||
**Primary active application:** `EnvelopeGenerator.Server`
|
|
||||||
|
|
||||||
`EnvelopeGenerator.Server` is the current runtime host and contains:
|
|
||||||
- Blazor server host
|
|
||||||
- WebAssembly host integration
|
|
||||||
- API controllers
|
|
||||||
- authentication/authorization setup
|
|
||||||
- Swagger/Scalar setup
|
|
||||||
- YARP reverse proxy configuration
|
|
||||||
- DevExpress server-side services
|
|
||||||
- SQL Server distributed cache setup
|
|
||||||
|
|
||||||
### Client Project
|
|
||||||
**Client UI project:** `EnvelopeGenerator.Server.Client`
|
|
||||||
|
|
||||||
This project contains:
|
|
||||||
- WebAssembly-rendered pages
|
|
||||||
- client-side services
|
|
||||||
- client models and options
|
|
||||||
- sender and receiver login flows
|
|
||||||
|
|
||||||
### Other Projects
|
|
||||||
- `EnvelopeGenerator.Application` — MediatR/CQRS handlers and business logic
|
|
||||||
- `EnvelopeGenerator.Domain` — domain models, constants, shared abstractions
|
|
||||||
- `EnvelopeGenerator.Infrastructure` — EF Core and infrastructure services
|
|
||||||
- `EnvelopeGenerator.PdfEditor` — PDF-related backend utilities
|
|
||||||
- `EnvelopeGenerator.API` — still exists in the solution, but the current merged app host is `EnvelopeGenerator.Server`
|
|
||||||
|
|
||||||
### Legacy / Do Not Touch
|
|
||||||
- `EnvelopeGenerator.Service`
|
|
||||||
- `EnvelopeGenerator.Form`
|
|
||||||
- `EnvelopeGenerator.BBTests`
|
|
||||||
- `EnvelopeGenerator.CommonServices`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Current Hosting Model
|
|
||||||
|
|
||||||
`EnvelopeGenerator.Server/Program.cs` currently configures:
|
|
||||||
- `AddRazorComponents()` with both interactive server and interactive WebAssembly components
|
|
||||||
- `AddControllers()` and `MapControllers()`
|
|
||||||
- JWT authentication for sender and receiver flows
|
|
||||||
- cookie authentication
|
|
||||||
- authorization policies using `AuthScheme.Sender`, `AuthScheme.Receiver`, `AuthPolicy.Sender`, `AuthPolicy.Receiver`
|
|
||||||
- `AddReverseProxy()` with `yarp.json`
|
|
||||||
- Swagger / OpenAPI / Scalar
|
|
||||||
- distributed SQL Server cache
|
|
||||||
- DevExpress Blazor and DevExpress PDF Viewer server-side services
|
|
||||||
- request localization middleware
|
|
||||||
|
|
||||||
This means the active app is a **merged UI + API host**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Reverse Proxy
|
|
||||||
|
|
||||||
**Config file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/yarp.json`
|
|
||||||
|
|
||||||
Current YARP usage is focused on **AuthHub forwarding**, not a general `/api/* -> EnvelopeGenerator.API` proxy.
|
|
||||||
|
|
||||||
Configured routes forward:
|
|
||||||
- `POST /api/auth` -> AuthHub `/api/auth/sign-flow`
|
|
||||||
- `POST /api/Auth/envelope-receiver/{key}` -> AuthHub `/api/auth/envelope-receiver/{key}?cookie=true`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Active Routes and Files
|
|
||||||
|
|
||||||
### WebAssembly Pages (`EnvelopeGenerator.Server.Client`)
|
|
||||||
| Route | File | Render Mode | Purpose |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `/` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/IndexPage.razor` | WebAssembly | Landing page |
|
|
||||||
| `/sender/login` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/LoginSenderPage.razor` | WebAssembly | Sender login |
|
|
||||||
| `/sender` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor` | WebAssembly (`prerender: false`) | Sender dashboard |
|
|
||||||
| `/envelope/login/{EnvelopeKey}` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/LoginReceiverPage.razor` | WebAssembly | Receiver login |
|
|
||||||
|
|
||||||
### Server Pages (`EnvelopeGenerator.Server`)
|
|
||||||
| Route | File | Render Mode | Purpose |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `/envelope/{EnvelopeKey}` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` | InteractiveServer | Main receiver PDF viewer and signing page |
|
|
||||||
| `/envelope/DxPdfViewer` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | InteractiveServer | DevExpress PDF Viewer test page |
|
|
||||||
| `/envelope/{EnvelopeKey}/DxReportViewer` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | InteractiveServer | DevExpress report-based PDF rendering |
|
|
||||||
| `/envelope/Embed` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_embed.razor` | InteractiveServer | Embedded browser PDF view test page |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Current API Location
|
|
||||||
|
|
||||||
The active application exposes controllers from:
|
|
||||||
`EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers`
|
|
||||||
|
|
||||||
Current controller set includes:
|
|
||||||
- `AnnotationController`
|
|
||||||
- `AuthController`
|
|
||||||
- `CacheController`
|
|
||||||
- `ConfigController`
|
|
||||||
- `DocumentController`
|
|
||||||
- `EmailTemplateController`
|
|
||||||
- `EnvelopeController`
|
|
||||||
- `EnvelopeReceiverController`
|
|
||||||
- `EnvelopeTypeController`
|
|
||||||
- `HistoryController`
|
|
||||||
- `LocalizationController`
|
|
||||||
- `ReadOnlyController`
|
|
||||||
- `ReceiverController`
|
|
||||||
- `SignatureController`
|
|
||||||
- `TfaRegistrationController`
|
|
||||||
|
|
||||||
Do not assume API behavior lives only in `EnvelopeGenerator.API`; the active merged host contains controller endpoints directly.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Authentication Model
|
|
||||||
|
|
||||||
### Sender
|
|
||||||
Client login page uses `EnvelopeGenerator.Server.Client/Services/AuthService.cs`.
|
|
||||||
|
|
||||||
Key sender endpoints:
|
|
||||||
- `POST /api/auth?cookie=true` — login
|
|
||||||
- `GET /api/auth/check` — current sender access check
|
|
||||||
- `POST /api/auth/logout` — logout
|
|
||||||
|
|
||||||
### Receiver
|
|
||||||
Receiver authentication is **per envelope**.
|
|
||||||
|
|
||||||
Key receiver endpoints used by client services:
|
|
||||||
- `POST /api/Auth/envelope-receiver/{envelopeKey}` — submit access code
|
|
||||||
- `GET /api/auth/check/envelope/{envelopeKey}` — check access
|
|
||||||
- `POST /api/auth/logout/envelope/{envelopeKey}` — logout receiver for one envelope
|
|
||||||
|
|
||||||
Receiver cookie resolution in server auth uses an envelope-specific cookie name derived from:
|
|
||||||
- `AuthTokenSignFLOWReceiver.{envelopeKey}` pattern
|
|
||||||
|
|
||||||
### Receiver Server-Side Authorization
|
|
||||||
`EnvelopeReceiverPage.razor` does **not** rely on its own API access-check call for page authorization.
|
|
||||||
|
|
||||||
It uses:
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs`
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
- tries the current `HttpContext.User`
|
|
||||||
- if needed, reads the per-envelope receiver cookie directly
|
|
||||||
- validates the JWT with the receiver auth scheme
|
|
||||||
- verifies the token subject matches the route envelope key
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Receiver Page Data Loading
|
|
||||||
|
|
||||||
Main server-side page data service:
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs`
|
|
||||||
|
|
||||||
This service loads directly via MediatR and distributed cache:
|
|
||||||
- document bytes
|
|
||||||
- receiver envelope data
|
|
||||||
- signature placeholders
|
|
||||||
- cached signature data
|
|
||||||
|
|
||||||
For signature placeholders, the service:
|
|
||||||
- reads document receiver elements
|
|
||||||
- filters them for the authenticated receiver
|
|
||||||
- converts coordinates to `UnitOfLength.Point` before UI use
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Receiver PDF Viewer
|
|
||||||
|
|
||||||
**Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor`
|
|
||||||
|
|
||||||
Current receiver viewer characteristics:
|
|
||||||
- route: `/envelope/{EnvelopeKey}`
|
|
||||||
- render mode: `InteractiveServer`
|
|
||||||
- PDF rendering: `PDF.js`
|
|
||||||
- toolbar: page navigation, zoom, thumbnail toggle, signature navigation, signature reset
|
|
||||||
- signature popup: `DxPopup`
|
|
||||||
- thumbnail sidebar: resizable and stored in `localStorage`
|
|
||||||
|
|
||||||
### JS Assets
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js`
|
|
||||||
|
|
||||||
### CSS
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css`
|
|
||||||
|
|
||||||
### PDF.js CDN
|
|
||||||
- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
|
|
||||||
- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Signature Workflow
|
|
||||||
|
|
||||||
Receiver signatures are handled as a **viewer overlay workflow**.
|
|
||||||
|
|
||||||
### Current behavior
|
|
||||||
1. Server-side authorization validates receiver access.
|
|
||||||
2. The page loads document bytes, receiver data, signature placeholders, and cached signature state.
|
|
||||||
3. If no cached signature exists, the signature popup opens automatically.
|
|
||||||
4. Receiver creates signature using one of three tabs:
|
|
||||||
- draw
|
|
||||||
- text
|
|
||||||
- image
|
|
||||||
5. Required metadata:
|
|
||||||
- full name
|
|
||||||
- place
|
|
||||||
6. Optional metadata:
|
|
||||||
- position
|
|
||||||
7. Clicking a signature placeholder applies the signature as a client-side overlay in the PDF viewer.
|
|
||||||
|
|
||||||
### Important note
|
|
||||||
Although `itext` is referenced by the server project, the current receiver page signing flow is **not PDF stamping-based**. The active receiver UI uses client-side overlay behavior in the viewer.
|
|
||||||
|
|
||||||
### Signature DTO
|
|
||||||
`EnvelopeGenerator.Server.Client/Models/SignatureCaptureDto.cs`
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public sealed record SignatureCaptureDto {
|
|
||||||
public required string DataUrl { get; init; }
|
|
||||||
public required string FullName { get; init; }
|
|
||||||
public string Position { get; init; } = "";
|
|
||||||
public required string Place { get; init; }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Signature Cache
|
|
||||||
|
|
||||||
### Active cache model
|
|
||||||
The current receiver page cache flow is handled directly in the server project through:
|
|
||||||
- `EnvelopeReceiverPageDataService`
|
|
||||||
- `IDistributedCache`
|
|
||||||
- SQL Server distributed cache configuration from `Program.cs`
|
|
||||||
|
|
||||||
### Cache key format
|
|
||||||
Current server-side key prefix:
|
|
||||||
- `envelope-generator.receiver-ui.signature:{receiverSignature}`
|
|
||||||
|
|
||||||
This is different from an envelope-key-only cache convention.
|
|
||||||
|
|
||||||
### Config
|
|
||||||
`EnvelopeGenerator.Server/EnvelopeGenerator.Server/Options/CacheOptions.cs`
|
|
||||||
- section name: `Cache`
|
|
||||||
- option: `SignatureCacheExpiration`
|
|
||||||
|
|
||||||
### Related controller
|
|
||||||
A cache API controller also exists in:
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/CacheController.cs`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sender Dashboard
|
|
||||||
|
|
||||||
**Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor`
|
|
||||||
|
|
||||||
Current behavior:
|
|
||||||
- checks sender access through `AuthService.CheckSenderAccessAsync()`
|
|
||||||
- redirects to `/sender/login` when unauthorized
|
|
||||||
- loads envelope list through client `EnvelopeService`
|
|
||||||
- separates envelopes into active/completed tabs
|
|
||||||
- uses `DevExpress DxGrid`
|
|
||||||
|
|
||||||
The sender page is active, but create/edit/delete actions are still marked with TODO behavior in the UI page.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Localization
|
|
||||||
|
|
||||||
Current server host localization setup in `Program.cs`:
|
|
||||||
- supported cultures: `de-DE`, `en-US`
|
|
||||||
- request localization middleware is enabled
|
|
||||||
- `QueryStringRequestCultureProvider` is added
|
|
||||||
- cookie-based localization services are registered via `AddCookieBasedLocalizer()`
|
|
||||||
|
|
||||||
Do not assume the old ReceiverUI-only `localStorage` culture approach is the current source of truth for the active host.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Coordinate System
|
|
||||||
|
|
||||||
### Source data
|
|
||||||
Database signature coordinates are still based on:
|
|
||||||
- **unit:** inches
|
|
||||||
- **origin:** top-left
|
|
||||||
- **axes:** X right, Y down
|
|
||||||
|
|
||||||
### Relevant conversions
|
|
||||||
- inches -> PDF points: `x_pt = x_inches * 72`
|
|
||||||
- inches -> DevExpress DX units: `x_dx = x_inches * 100`
|
|
||||||
|
|
||||||
### Current receiver page behavior
|
|
||||||
The server page data service converts signature placeholders to **points** before sending them into the viewer workflow.
|
|
||||||
|
|
||||||
### Unit systems to keep in mind
|
|
||||||
| System | Unit | Origin | Y-axis |
|
|
||||||
|---|---|---|---|
|
|
||||||
| Database | Inches | Top-left | Down |
|
|
||||||
| PDF.js display | Pixels | Top-left | Down |
|
|
||||||
| PDF points | Points | Depends on PDF model | Depends on consumer |
|
|
||||||
| DevExpress DX | 1/100 inch style coordinates | Top-left-oriented usage in this app | Down-oriented usage |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key Services and Files
|
|
||||||
|
|
||||||
### Client services
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocumentService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureCacheService.cs`
|
|
||||||
|
|
||||||
### Server services
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs`
|
|
||||||
|
|
||||||
### Server config and host files
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/yarp.json`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Working Rules for This Workspace
|
|
||||||
|
|
||||||
- Treat `EnvelopeGenerator.Server` as the active main application host.
|
|
||||||
- Treat `EnvelopeGenerator.Server.Client` as the active client UI project.
|
|
||||||
- Prefer current `Server` / `Server.Client` paths over old `WebUI` / `ReceiverUI` references.
|
|
||||||
- Do not use `EnvelopeGenerator.Web` or `EnvelopeGenerator.ReceiverUI` as the primary implementation target unless explicitly asked.
|
|
||||||
- Do not modify the legacy VB.NET projects unless explicitly requested.
|
|
||||||
- For receiver PDF/signature work, prefer the current `PDF.js`-based flow in `EnvelopeReceiverPage.razor`.
|
|
||||||
- For DevExpress PDF viewer issues, remember server-side services are registered in `EnvelopeGenerator.Server`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Last Updated:** 2026-06-29
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Commands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TCommand"></typeparam>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class CreateCommandHandler<TCommand, TEntity> : IRequestHandler<TCommand, TEntity>
|
|
||||||
where TCommand : class, IRequest<TEntity>
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
protected readonly IRepository<TEntity> Repository;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public CreateCommandHandler(IRepository<TEntity> repository)
|
|
||||||
{
|
|
||||||
Repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Task<TEntity> Handle(TCommand request, CancellationToken cancel) => Repository.CreateAsync(request, cancel);
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using MediatR;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Commands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TUpdateDto"></typeparam>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public abstract record UpdateCommand<TUpdateDto, TEntity> : IRequest where TUpdateDto : class where TEntity : class
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public TUpdateDto Update { get; init; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public abstract Expression<Func<TEntity, bool>> BuildQueryExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TCommand"></typeparam>
|
|
||||||
/// <typeparam name="TUpdateDto"></typeparam>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class UpdateCommandHandler<TCommand, TUpdateDto, TEntity> : IRequestHandler<TCommand>
|
|
||||||
where TUpdateDto : class
|
|
||||||
where TEntity : class
|
|
||||||
where TCommand : UpdateCommand<TUpdateDto, TEntity>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
protected readonly IRepository<TEntity> Repository;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public UpdateCommandHandler(IRepository<TEntity> repository)
|
|
||||||
{
|
|
||||||
Repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Task Handle(TCommand request, CancellationToken cancel)
|
|
||||||
=> Repository.UpdateAsync(request.Update, request.BuildQueryExpression(), cancel);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class AuthenticatorParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789012345678901234567890123456789'
|
|
||||||
/// </summary>
|
|
||||||
public string CharPool { get; init; } = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789012345678901234567890123456789";
|
|
||||||
|
|
||||||
//TODO: Increase the DefaultTotpSecretKeyLength (e.g. to 32) but make sure that the QR code is generated correctly and can be scanned by the authenticator.
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 20
|
|
||||||
/// </summary>
|
|
||||||
public int DefaultTotpSecretKeyLength { get; init; } = 20;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is
|
|
||||||
/// </summary>
|
|
||||||
public string TotpIssuer { get; init; } = "signFlow";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 0 is user email, 1 is secret key and 2 is issuer.
|
|
||||||
/// </summary>
|
|
||||||
public string TotpUrlFormat { get; init; } = "otpauth://totp/{0}?secret={1}&issuer={2}";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 20.
|
|
||||||
/// </summary>
|
|
||||||
public int TotpQRPixelsPerModule { get; init; } = 20;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class DispatcherParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 1
|
|
||||||
/// </summary>
|
|
||||||
public int SendingProfile { get; init; } = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defalt value is 'DDEnvelopGenerator'
|
|
||||||
/// </summary>
|
|
||||||
public string AddedWho { get; init; } = "DDEnvelopGenerator";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 202377
|
|
||||||
/// </summary>
|
|
||||||
public int ReminderTypeId { get; init; } = 202377;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
public string? EmailAttmt1 { get; init; } = null;
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using DigitalData.Core.Client.Interface;
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// https://www.gtx-messaging.com/en/api-docs/sms-rest-api/
|
|
||||||
/// </summary>
|
|
||||||
public class GtxMessagingParams : IHttpClientOptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required string Uri { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? Path { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, object>? Headers { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, object?>? QueryParams { get; init; }
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'to'
|
|
||||||
/// </summary>
|
|
||||||
public string RecipientQueryParamName { get; init; } = "to";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'text'
|
|
||||||
/// </summary>
|
|
||||||
public string MessageQueryParamName { get; init; } = "text";
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class MailParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required Dictionary<string, string> Placeholders { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
using OtpNet;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class TotpSmsParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The unit is second.
|
|
||||||
/// </summary>
|
|
||||||
public int TotpStep { get; init; } = 90;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'Ihr 2FA-Passwort lautet {0}. Gültig bis {1}'
|
|
||||||
/// </summary>
|
|
||||||
public string Format { get; init; } = "Ihr 2FA-Passwort lautet {0}. Gültig bis {1}";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public ExpirationHandler Expiration { get; init; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is VerificationWindow.RfcSpecifiedNetworkDelay
|
|
||||||
/// </summary>
|
|
||||||
public VerificationWindow? TotpVerificationWindow { get; private init; } = VerificationWindow.RfcSpecifiedNetworkDelay;
|
|
||||||
|
|
||||||
private IEnumerable<int>? _tvwParams;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<int>? TotpVerificationWindowParams
|
|
||||||
{
|
|
||||||
get => _tvwParams;
|
|
||||||
init
|
|
||||||
{
|
|
||||||
_tvwParams = value;
|
|
||||||
if(_tvwParams is not null)
|
|
||||||
TotpVerificationWindow = new(previous: _tvwParams.ElementAtOrDefault(0), future: _tvwParams.ElementAtOrDefault(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class ExpirationHandler
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'e{0}_r{1}_sms_code_expiration'
|
|
||||||
/// </summary>
|
|
||||||
public string CacheKeyFormat { get; init; } = "e{0}_r{1}_sms_code_expiration";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is HH:mm:ss
|
|
||||||
/// </summary>
|
|
||||||
public string Format { get; init; } = "HH:mm:ss";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'de-DE'
|
|
||||||
/// </summary>
|
|
||||||
public string CultureName
|
|
||||||
{
|
|
||||||
get => _cultureInfo.Name;
|
|
||||||
init => _cultureInfo = new(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'de-DE'
|
|
||||||
/// </summary>
|
|
||||||
private CultureInfo _cultureInfo = new("de-DE");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'de-DE'
|
|
||||||
/// </summary>
|
|
||||||
public CultureInfo CultureInfo => _cultureInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record AnnotationCreateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Not required for DevExpress")]
|
|
||||||
public int ElementId { get; init; } = -1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Not required for DevExpress")]
|
|
||||||
public string Name { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Not required for DevExpress")]
|
|
||||||
public string Value { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Not required for DevExpress")]
|
|
||||||
public string Type { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Horizontal position of the signature field on the page.
|
|
||||||
/// <br/><br/>
|
|
||||||
/// <b>Unit:</b> INCHES (GdPicture14 native), origin at the <b>top-left</b> corner of the page, X increases to the right.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to DevExpress:</b> Multiply by 100 (DX uses 1/100 inch).
|
|
||||||
/// Convert: <c>xDX = xInches * 100.0</c>
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to PDF Points:</b> Multiply by 72 (PSPDFKit, iText7 use 1/72 inch).
|
|
||||||
/// Convert: <c>xPt = xInches * 72.0</c>
|
|
||||||
/// </summary>
|
|
||||||
public double? X { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Vertical position of the signature field on the page.
|
|
||||||
/// <br/><br/>
|
|
||||||
/// <b>Unit:</b> INCHES (GdPicture14 native), origin at the <b>top-left</b> corner of the page, Y increases downward.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to DevExpress:</b> Multiply by 100 (DX uses 1/100 inch).
|
|
||||||
/// Convert: <c>yDX = yInches * 100.0</c>
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to PDF Points (top-left origin):</b> Multiply by 72.
|
|
||||||
/// Convert: <c>yPt = yInches * 72.0</c>
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to PDF Points (bottom-left origin - iText7):</b> Y-flip required.
|
|
||||||
/// Convert: <c>yPt = (pageHeightInches - yInches - elemHeightInches) * 72.0</c>
|
|
||||||
/// </summary>
|
|
||||||
public double? Y { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Not required for DevExpress")]
|
|
||||||
public double? Width { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Not required for DevExpress")]
|
|
||||||
public double? Height { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Added to eliminate the need for SignatureDto in DevExpress
|
|
||||||
/// </summary>
|
|
||||||
public int? Page { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record AnnotationDto : AnnotationCreateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public long Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? ChangedWho { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing configuration settings.
|
|
||||||
/// </summary>
|
|
||||||
public class ConfigDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the sending profile identifier.
|
|
||||||
/// </summary>
|
|
||||||
public int SendingProfile { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the signature host URL or name.
|
|
||||||
/// </summary>
|
|
||||||
public required string SignatureHost { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name of the external program.
|
|
||||||
/// </summary>
|
|
||||||
public string? ExternalProgramName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the path where exports will be saved.
|
|
||||||
/// </summary>
|
|
||||||
public string? ExportPath { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Interfaces;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing a positioned element assigned to a document receiver.
|
|
||||||
/// </summary>
|
|
||||||
public class DocReceiverElementDto : IDocReceiverElement
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the element.
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the identifier of the associated document.
|
|
||||||
/// </summary>
|
|
||||||
public int DocumentId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the identifier of the receiver.
|
|
||||||
/// </summary>
|
|
||||||
public int ReceiverId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the type of the element.
|
|
||||||
/// </summary>
|
|
||||||
public int ElementType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the X coordinate of the element.
|
|
||||||
/// </summary>
|
|
||||||
public double X { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the Y coordinate of the element.
|
|
||||||
/// </summary>
|
|
||||||
public double Y { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the width of the element.
|
|
||||||
/// </summary>
|
|
||||||
public double Width { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the height of the element.
|
|
||||||
/// </summary>
|
|
||||||
public double Height { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the page number where the element appears.
|
|
||||||
/// </summary>
|
|
||||||
public int Page { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the element is required.
|
|
||||||
/// </summary>
|
|
||||||
public bool Required { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the tooltip text for the element.
|
|
||||||
/// </summary>
|
|
||||||
public string? Tooltip { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether the element is read-only.
|
|
||||||
/// </summary>
|
|
||||||
public bool ReadOnly { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the annotation index for ordering or reference.
|
|
||||||
/// </summary>
|
|
||||||
public int AnnotationIndex { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when the element was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when the element was last changed, if applicable.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the top position of the element (in layout terms).
|
|
||||||
/// </summary>
|
|
||||||
public double Top => Y;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the left position of the element (in layout terms).
|
|
||||||
/// </summary>
|
|
||||||
public double Left => X;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<AnnotationDto>? Annotations { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public SenderAppType SenderAppType { get; set; } = SenderAppType.LegacyFormApp;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? FullName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? Position { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? Place { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public byte[]? Ink { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing a document within an envelope, including optional binary data and form elements.
|
|
||||||
/// </summary>
|
|
||||||
public class DocumentDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the document.
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the envelope ID to which the document belongs.
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the date and time when the document was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the binary data of the document, if available.
|
|
||||||
/// </summary>
|
|
||||||
public byte[]? ByteData { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the collection of elements associated with the document for receiver interactions, if any.
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<DocReceiverElementDto>? Elements { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing the status of a document for a specific receiver.
|
|
||||||
/// </summary>
|
|
||||||
public class DocumentStatusDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the document status entry.
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the ID of the associated envelope.
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the ID of the receiver associated with this status.
|
|
||||||
/// </summary>
|
|
||||||
public int ReceiverId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the current status code.
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeStatus Status { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when the status was changed.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? StatusChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when this record was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when this record was last changed.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the display value associated with the status.
|
|
||||||
/// </summary>
|
|
||||||
public string? Value { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record EmailTemplateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein.
|
|
||||||
/// </summary>
|
|
||||||
public string? Body { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Der Betreff der E-Mail-Vorlage. Kann null sein.
|
|
||||||
/// </summary>
|
|
||||||
public string? Subject { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Der Sprachcode der E-Mail-Vorlage.
|
|
||||||
/// </summary>
|
|
||||||
public string LangCode { get; set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
};
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
|
||||||
using DigitalData.UserManager.Application.DTOs.User;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.History;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using EnvelopeGenerator.Domain.Interfaces;
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record EnvelopeDto : IEnvelope
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int UserId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required EnvelopeStatus Status { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
public string StatusName { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
public string Uuid { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
[TemplatePlaceholder("[MESSAGE]")]
|
|
||||||
public string Message { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is string.Empty
|
|
||||||
/// </summary>
|
|
||||||
[TemplatePlaceholder("[DOCUMENT_TITLE]")]
|
|
||||||
public string Title { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int? ContractType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is 'de-DE'
|
|
||||||
/// </summary>
|
|
||||||
public string Language { get; set; } = "de-DE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int? EnvelopeTypeId { get; set; }
|
|
||||||
|
|
||||||
// TODO: use ReadAndConfirm property name
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use EnvelopeExtensions.IsReadAndConfirm extension metot instead.")]
|
|
||||||
public bool ReadOnly => this.IsReadAndConfirm();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int? CertificationType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool UseAccessCode { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool TFAEnabled { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public UserReadDto? User { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeType? EnvelopeType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? EnvelopeTypeTitle { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool IsAlreadySent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public byte[]? DocResult { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<DocumentDto>? Documents { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<EnvelopeReceiverDto>? EnvelopeReceivers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Envelope history entries tracking actions like DocumentSigned, EnvelopeOpened, etc.
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<HistoryDto>? Histories { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record EnvelopeReceiverDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeDto? Envelope { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public ReceiverDto? Receiver { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public (int Envelope, int Receiver) Id => (Envelope: EnvelopeId, Receiver: ReceiverId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int ReceiverId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int Sequence { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[TemplatePlaceholder("[NAME_RECEIVER]")]
|
|
||||||
public string? Name { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? JobTitle { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? CompanyName { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? PrivateMessage { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool HasPhoneNumber { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether this receiver has signed the envelope.
|
|
||||||
/// Checks if there is a DocumentSigned history entry for this receiver in the envelope's history.
|
|
||||||
/// </summary>
|
|
||||||
public bool Signed => Envelope?.Histories?.Any(h =>
|
|
||||||
h.Receiver?.Id == ReceiverId &&
|
|
||||||
h.Status == EnvelopeStatus.DocumentSigned
|
|
||||||
) ?? false;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record EnvelopeReceiverSecretDto : EnvelopeReceiverDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? AccessCode { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? PhoneNumber { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="DateValid"></param>
|
|
||||||
public record EnvelopeReceiverReadOnlyCreateDto(
|
|
||||||
DateTime DateValid)
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[EmailAddress]
|
|
||||||
[Required]
|
|
||||||
public required string ReceiverMail { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is null
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public long? EnvelopeId { get; set; } = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public string? AddedWho { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default value is DateTime.Now
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public DateTime AddedWhen { get; } = DateTime.Now;
|
|
||||||
};
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a read-only Data Transfer Object (DTO) for an envelope receiver.
|
|
||||||
/// Contains information about the receiver, associated envelope, and audit details.
|
|
||||||
/// </summary>
|
|
||||||
public class EnvelopeReceiverReadOnlyDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the unique identifier of the envelope receiver.
|
|
||||||
/// </summary>
|
|
||||||
public long Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the identifier of the associated envelope.
|
|
||||||
/// </summary>
|
|
||||||
public long EnvelopeId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the email address of the receiver.
|
|
||||||
/// </summary>
|
|
||||||
public required string ReceiverMail { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the date until which the receiver is valid.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime DateValid { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the date and time when the receiver was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the user who added the receiver.
|
|
||||||
/// Default value is 'unknown'.
|
|
||||||
/// </summary>
|
|
||||||
public string AddedWho { get; init; } = "Unknown";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the associated envelope details.
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeDto? Envelope { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the user who last changed the receiver, if any.
|
|
||||||
/// </summary>
|
|
||||||
public string? ChangedWho { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the date and time when the receiver was last changed, if any.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or inits the associated receiver details.
|
|
||||||
/// </summary>
|
|
||||||
public ReceiverDto? Receiver { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object for updating a read-only envelope receiver.
|
|
||||||
/// </summary>
|
|
||||||
public class EnvelopeReceiverReadOnlyUpdateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the envelope receiver.
|
|
||||||
/// </summary>
|
|
||||||
public long Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the date when the envelope receiver becomes valid.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime DateValid { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name of the user who made the change.
|
|
||||||
/// Default value is unknown.
|
|
||||||
/// </summary>
|
|
||||||
public string ChangedWho { get; set; } = "Unknown";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the date and time when the change was made.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime ChangedWhen { get; set; } = DateTime.Now;
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object representing a type of envelope with its configuration settings.
|
|
||||||
/// </summary>
|
|
||||||
public class EnvelopeTypeDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the unique identifier of the envelope type.
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the title of the envelope type.
|
|
||||||
/// </summary>
|
|
||||||
public string Title { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the language code used in this envelope type.
|
|
||||||
/// </summary>
|
|
||||||
public string Language { get; set; } = "de-DE";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of days after which the envelope expires.
|
|
||||||
/// </summary>
|
|
||||||
public int? ExpiresDays { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the certification type identifier.
|
|
||||||
/// </summary>
|
|
||||||
public int? CertificationType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether an access code is required.
|
|
||||||
/// </summary>
|
|
||||||
public bool? UseAccessCode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the final email template ID to be sent to the creator.
|
|
||||||
/// </summary>
|
|
||||||
public int? FinalEmailToCreator { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the final email template ID to be sent to the receivers.
|
|
||||||
/// </summary>
|
|
||||||
public int? FinalEmailToReceivers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when this envelope type was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the timestamp when this envelope type was last changed.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of days before expiry when a warning should be sent.
|
|
||||||
/// </summary>
|
|
||||||
public int? ExpiresWarningDays { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether reminder emails should be sent.
|
|
||||||
/// </summary>
|
|
||||||
public bool? SendReminderEmails { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of days before the first reminder is sent.
|
|
||||||
/// </summary>
|
|
||||||
public int? FirstReminderDays { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the interval in days between reminder emails.
|
|
||||||
/// </summary>
|
|
||||||
public int? ReminderIntervalDays { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the contract type associated with the envelope type.
|
|
||||||
/// </summary>
|
|
||||||
public int? ContractType { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.History;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Transfer Object for creating a new envelope history record.
|
|
||||||
/// </summary>
|
|
||||||
public class HistoryCreateDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the identifier of the envelope.
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the user reference associated with the action.
|
|
||||||
/// </summary>
|
|
||||||
public required string UserReference { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the status of the envelope at the time of the action.
|
|
||||||
/// </summary>
|
|
||||||
public int Status { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the date and time when the action occurred.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ActionDate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an optional comment related to the action.
|
|
||||||
/// </summary>
|
|
||||||
public string? Comment { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
using DigitalData.UserManager.Application.DTOs.User;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
|
|
||||||
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 HistoryDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Unique identifier for the envelope history entry.
|
|
||||||
/// </summary>
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Identifier of the associated envelope.
|
|
||||||
/// </summary>
|
|
||||||
public int EnvelopeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reference string for the user related to this history entry.
|
|
||||||
/// </summary>
|
|
||||||
public required string UserReference { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Include code of the envelope at this history point.
|
|
||||||
/// </summary>
|
|
||||||
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.
|
|
||||||
/// </summary>
|
|
||||||
public string? StatusName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Date and time when this history entry was added.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Date and time when an action was performed, if applicable.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ActionDate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Information about the sender associated with this history entry.
|
|
||||||
/// </summary>
|
|
||||||
public UserCreateDto? Sender { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Information about the receiver associated with this history entry.
|
|
||||||
/// </summary>
|
|
||||||
public ReceiverDto? Receiver { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Optional comment related to this history entry.
|
|
||||||
/// </summary>
|
|
||||||
public string? Comment { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override int GetHashCode() => Id.GetHashCode();
|
|
||||||
};
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class GtxMessagingResponse : Dictionary<string, object?> { }
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record SmsResponse
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required bool Ok { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns !Ok
|
|
||||||
/// </summary>
|
|
||||||
public bool Failed => !Ok;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public dynamic? Errors { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using System.Dynamic;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents PSPDFKit annotation data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Instant">Instant annotation data.</param>
|
|
||||||
/// <param name="Structured">Structured annotation data.</param>
|
|
||||||
[Obsolete("The PSPDFKit library is deprecated.")]
|
|
||||||
public record PsPdfKitAnnotation(ExpandoObject Instant, IEnumerable<AnnotationCreateDto> Structured);
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class ReceiverDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required string EmailAddress { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public required string Signature { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public string? TotpSecretkey { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public IEnumerable<EnvelopeReceiverDto>? EnvelopeReceivers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? LastUsedName => EnvelopeReceivers?.LastOrDefault()?.Name;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? TfaRegDeadline { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return Id.GetHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a captured signature with metadata created by the receiver in the signature popup.
|
|
||||||
/// This model holds the signature image (as base64 data URL) along with signer information
|
|
||||||
/// used for rendering applied signatures on the PDF canvas.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <b>Used in:</b> EnvelopeViewer.razor signature popup workflow
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Creation:</b> User draws/types/uploads signature and fills required fields
|
|
||||||
/// </remarks>
|
|
||||||
public sealed record Signature
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// TBDD_DOCUMENT_RECEIVER_ELEMENT.ID - identifies the specific signature field on the PDF page.
|
|
||||||
/// </summary>
|
|
||||||
public required int Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Base64-encoded data URL of the signature image.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Format:</b> <c>data:image/png;base64,iVBORw0KG...</c>
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Source:</b> Canvas.toDataURL() from signature pad (draw/text/image tabs)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Usage:</b> Set as <c>img.src</c> in applied signature overlay
|
|
||||||
/// </summary>
|
|
||||||
public required string DataUrl { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Full name of the signer (first and last name).
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Required:</b> Yes (validated in popup)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Example:</b> "Max Mustermann"
|
|
||||||
/// </summary>
|
|
||||||
public required string FullName { get; init; }
|
|
||||||
|
|
||||||
private readonly string? _position = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Job title or position of the signer.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Required:</b> No (optional field)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Example:</b> "Geschäftsführer" or empty string
|
|
||||||
/// </summary>
|
|
||||||
public string? Position
|
|
||||||
{
|
|
||||||
get => _position;
|
|
||||||
init => _position = string.IsNullOrWhiteSpace(value) ? null : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Location/place where the signature was created.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Required:</b> Yes (validated in popup)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Display:</b> Shown with current date in German format (dd.MM.yyyy)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Example:</b> "Berlin" ? rendered as "Berlin, 26.01.2025"
|
|
||||||
/// </summary>
|
|
||||||
public required string Place { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a user's UI preferences stored in the distributed cache (dbo.TBDD_CACHE).
|
|
||||||
/// </summary>
|
|
||||||
public sealed record UserPreferencesDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The active DevExpress theme name.
|
|
||||||
/// Allowed values: "blazing-berry", "fluent", "purple"
|
|
||||||
/// </summary>
|
|
||||||
public string ThemeName { get; set; } = "blazing-berry";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether dark mode is active.
|
|
||||||
/// Default: false (light mode).
|
|
||||||
/// </summary>
|
|
||||||
public bool IsDarkMode { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Persisted DxGrid layout states, keyed by a stable grid identifier.
|
|
||||||
/// Example keys: "sender.active-envelopes", "sender.completed-envelopes"
|
|
||||||
/// Values are opaque JSON strings produced by DxGrid.SaveLayoutAsync().
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, string> GridLayouts { get; set; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The authenticated user's display name ("{GivenName} {Surname}").
|
|
||||||
/// Populated by the server on GET responses from JWT claims.
|
|
||||||
/// NOT persisted in cache — injected at read time by UserPreferencesController.
|
|
||||||
/// Ignored on PUT requests.
|
|
||||||
/// </summary>
|
|
||||||
public string? DisplayName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static UserPreferencesDto Default { get; } = new UserPreferencesDto();
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public enum EnvelopeFlag
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
EnvelopeOrReceiverNonexists,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
NonDecodableEnvelopeReceiverId,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
WrongEnvelopeReceiverId,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
AccessCodeNull
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using EnvelopeGenerator.Domain.Interfaces.Auditing;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extension methods for applying auditing timestamps during AutoMapper mappings.
|
|
||||||
/// </summary>
|
|
||||||
public static class AutoMapperAuditingExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Maps <see cref="IHasAddedWhen.AddedWhen"/> to the current UTC time.
|
|
||||||
/// </summary>
|
|
||||||
public static IMappingExpression<TSource, TDestination> MapAddedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
|
||||||
where TDestination : IHasAddedWhen
|
|
||||||
=> expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.Now));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maps <see cref="IHasChangedWhen.ChangedWhen"/> to the current UTC time.
|
|
||||||
/// </summary>
|
|
||||||
public static IMappingExpression<TSource, TDestination> MapChangedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
|
||||||
where TDestination : IHasChangedWhen
|
|
||||||
=> expression.ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(_ => DateTime.Now));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a base64 data URL string to a byte array.
|
|
||||||
/// Handles data URLs in the format: "data:image/png;base64,iVBORw0KG..."
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataUrl">The base64 data URL string from Canvas.toDataURL()</param>
|
|
||||||
/// <returns>The decoded byte array, or null if the input is null or empty</returns>
|
|
||||||
public static byte[]? MapDataUrlToRequiredBytes(this string dataUrl)
|
|
||||||
{
|
|
||||||
// Remove data URL prefix (e.g., "data:image/png;base64,")
|
|
||||||
var base64Index = dataUrl.IndexOf(',', StringComparison.Ordinal);
|
|
||||||
if (base64Index == -1)
|
|
||||||
throw new ArgumentException("Invalid data URL format. Unable to extract base64 data.", nameof(dataUrl));
|
|
||||||
|
|
||||||
var base64Data = dataUrl[(base64Index + 1)..];
|
|
||||||
return Convert.FromBase64String(base64Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
using Microsoft.Extensions.Caching.Distributed;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class CacheExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Task SetLongAsync(this IDistributedCache cache, string key, long value, DistributedCacheEntryOptions? options = null, CancellationToken cToken = default)
|
|
||||||
=> options is null
|
|
||||||
? cache.SetAsync(key, BitConverter.GetBytes(value), token: cToken)
|
|
||||||
: cache.SetAsync(key, BitConverter.GetBytes(value), options: options, token: cToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<long?> GetLongAsync(this IDistributedCache cache, string key, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetAsync(key, cToken);
|
|
||||||
return value is null ? null : BitConverter.ToInt64(value, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Task SetDateTimeAsync(this IDistributedCache cache, string key, DateTime value, DistributedCacheEntryOptions? options = null, CancellationToken cToken = default)
|
|
||||||
=> cache.SetLongAsync(key: key, value: value.Ticks, options: options, cToken: cToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<DateTime?> GetDateTimeAsync(this IDistributedCache cache, string key, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetAsync(key, cToken);
|
|
||||||
return value is null ? null : new(BitConverter.ToInt64(value, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Task SetTimeSpanAsync(this IDistributedCache cache, string key, TimeSpan value, DistributedCacheEntryOptions? options = null, CancellationToken cToken = default)
|
|
||||||
=> cache.SetLongAsync(key: key, value: value.Ticks, options: options, cToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<TimeSpan?> GetTimeSpanAsync(this IDistributedCache cache, string key, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetAsync(key, cToken);
|
|
||||||
return value is null ? null : new(BitConverter.ToInt64(value, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: use code generator
|
|
||||||
#region GetOrSetAsync
|
|
||||||
|
|
||||||
#region string
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="factory"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cacheInBackground"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<string> GetOrSetAsync(this IDistributedCache cache, string key, Func<string> factory, DistributedCacheEntryOptions? options = null, bool cacheInBackground = false, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetStringAsync(key, cToken);
|
|
||||||
if (value is null)
|
|
||||||
{
|
|
||||||
// create new and save
|
|
||||||
value = factory();
|
|
||||||
|
|
||||||
Task CacheAsync() => options is null
|
|
||||||
? cache.SetStringAsync(key, value, cToken)
|
|
||||||
: cache.SetStringAsync(key, value, options, cToken);
|
|
||||||
|
|
||||||
if (cacheInBackground)
|
|
||||||
_ = Task.Run(async () => await CacheAsync(), cToken);
|
|
||||||
else
|
|
||||||
await CacheAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="factoryAsync"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cacheInBackground"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<string> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<string>> factoryAsync, DistributedCacheEntryOptions? options = null, bool cacheInBackground = false, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
var value = await cache.GetStringAsync(key, cToken);
|
|
||||||
if(value is null)
|
|
||||||
{
|
|
||||||
// create new and save
|
|
||||||
value = await factoryAsync();
|
|
||||||
|
|
||||||
Task CacheAsync() => options is null
|
|
||||||
? cache.SetStringAsync(key: key, value: value, token: cToken)
|
|
||||||
: cache.SetStringAsync(key: key, value: value, options: options, token: cToken);
|
|
||||||
|
|
||||||
if (cacheInBackground)
|
|
||||||
_ = Task.Run(async () => await CacheAsync(), cToken);
|
|
||||||
else
|
|
||||||
await CacheAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region DateTime
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="factory"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cacheInBackground"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<DateTime> GetOrSetAsync(this IDistributedCache cache, string key, Func<DateTime> factory, DistributedCacheEntryOptions? options = null, bool cacheInBackground = false, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
if (await cache.GetDateTimeAsync(key, cToken) is DateTime dateTimeValue)
|
|
||||||
return dateTimeValue;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// create new and save
|
|
||||||
var newValue = factory();
|
|
||||||
|
|
||||||
Task CacheAsync() => options is null
|
|
||||||
? cache.SetDateTimeAsync(key, newValue, cToken: cToken)
|
|
||||||
: cache.SetDateTimeAsync(key, newValue, options, cToken);
|
|
||||||
|
|
||||||
if (cacheInBackground)
|
|
||||||
_ = Task.Run(async () => await CacheAsync(), cToken);
|
|
||||||
else
|
|
||||||
await CacheAsync();
|
|
||||||
|
|
||||||
return newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cache"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <param name="factory"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <param name="cacheInBackground"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<DateTime> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<DateTime>> factory, DistributedCacheEntryOptions? options = null, bool cacheInBackground = false, CancellationToken cToken = default)
|
|
||||||
{
|
|
||||||
if (await cache.GetDateTimeAsync(key, cToken) is DateTime dateTimeValue)
|
|
||||||
return dateTimeValue;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// create new and save
|
|
||||||
var newValue = await factory();
|
|
||||||
|
|
||||||
Task CacheAsync() => options is null
|
|
||||||
? cache.SetDateTimeAsync(key, newValue, cToken: cToken)
|
|
||||||
: cache.SetDateTimeAsync(key, newValue, options, cToken);
|
|
||||||
|
|
||||||
if (cacheInBackground)
|
|
||||||
_ = Task.Run(async () => await CacheAsync(), cToken);
|
|
||||||
else
|
|
||||||
await CacheAsync();
|
|
||||||
|
|
||||||
return newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class JsonExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ToJson(this object obj, JsonSerializerOptions? options = null)
|
|
||||||
=> JsonSerializer.Serialize(obj, options);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for common mapping and conversion operations.
|
|
||||||
/// </summary>
|
|
||||||
public static class MappingExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the response indicates a successful "OK" message status.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="gtxMessagingResponse">The response object to evaluate.</param>
|
|
||||||
/// <returns><see langword="true"/> if the response contains a "message-status" key with a value of "ok" (case-insensitive);
|
|
||||||
/// otherwise, <see langword="false"/>.</returns>
|
|
||||||
public static bool Ok(this GtxMessagingResponse gtxMessagingResponse)
|
|
||||||
=> gtxMessagingResponse.TryGetValue("message-status", out var status)
|
|
||||||
&& status?.ToString()?.ToLower() == "ok";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the specified byte array to its equivalent string representation encoded in base-64.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">The byte array to encode.</param>
|
|
||||||
/// <returns>A base-64 encoded string representation of the input byte array.</returns>
|
|
||||||
public static string ToBase64String(this byte[] bytes)
|
|
||||||
=> Convert.ToBase64String(bytes);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
|
|
||||||
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, _ =>
|
|
||||||
{
|
|
||||||
var mergedIgnores = new List<TEnum>();
|
|
||||||
|
|
||||||
foreach (var ignore in ignores)
|
|
||||||
{
|
|
||||||
if (ignore is IEnumerable<TEnum> ignoreList)
|
|
||||||
mergedIgnores.AddRange(ignoreList);
|
|
||||||
else if (ignore is TEnum ignoreVal)
|
|
||||||
mergedIgnores.Add(ignoreVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Enum.GetValues(typeof(TEnum))
|
|
||||||
.Cast<TEnum>()
|
|
||||||
.Where(e => !mergedIgnores.Contains(e))
|
|
||||||
.ToDictionary(e => e.ToString(), e => Convert.ToInt32(e));
|
|
||||||
})
|
|
||||||
?? throw new InvalidOperationException($"Failed to cache or retrieve enum dictionary for type '{typeof(TEnum).FullName}'.");
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using OtpNet;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class StringExtension
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="totp"></param>
|
|
||||||
/// <param name="secret"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static bool IsValidTotp(this string totp, string secret)
|
|
||||||
{
|
|
||||||
var secret_bytes = Base32Encoding.ToBytes(secret);
|
|
||||||
var secret_totp = new Totp(secret_bytes);
|
|
||||||
return secret_totp.VerifyTotp(totp, out _, VerificationWindow.RfcSpecifiedNetworkDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="seperator"></param>
|
|
||||||
/// <param name="values"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string Join(this IEnumerable<string> values, string seperator) => string.Join(seperator, values);
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
using DigitalData.Core.Exceptions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extension methods for tasks
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
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>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
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>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
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>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
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>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="task"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static Task<T?> FirstOrDefaultAsync<T>(this Task<IEnumerable<T>> task) => task.Then(t => t.FirstOrDefault());
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TException"></typeparam>
|
|
||||||
/// <param name="task"></param>
|
|
||||||
/// <param name="factory"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Task<T> FirstAsync<T, TException>(this Task<IEnumerable<T>> task, Func<TException> factory)
|
|
||||||
where TException : Exception
|
|
||||||
=> task.Then(t => t.FirstOrDefault() ?? throw factory());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class Exceptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static NotFoundException NotFound() => new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static BadRequestException BadRequest() => new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static ForbiddenException Forbidden() => new();
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using Ganss.Xss;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using System.Text.Encodings.Web;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class XSSExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="encoder"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TryEncode(this string? value, UrlEncoder encoder) => value is null ? value : encoder.Encode(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="encoder"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TryEncode(this LocalizedString? value, UrlEncoder encoder) => value is null ? null : encoder.Encode(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="html"></param>
|
|
||||||
/// <param name="sanitizer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TrySanitize(this string? html, HtmlSanitizer sanitizer) => html is null ? html : sanitizer.Sanitize(html);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="html"></param>
|
|
||||||
/// <param name="sanitizer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TrySanitize(this LocalizedString? html, HtmlSanitizer sanitizer) => html is null ? null : sanitizer.Sanitize(html);
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IConfigRepository : ICRUDRepository<Config, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Config?> ReadFirstAsync();
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IDocumentReceiverElementRepository : ICRUDRepository<DocReceiverElement, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IDocumentStatusRepository : ICRUDRepository<DocumentStatus, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEmailTemplateRepository : ICRUDRepository<EmailTemplate, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EmailTemplate?> ReadByNameAsync(EmailTemplateType type);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeDocumentRepository : ICRUDRepository<Document, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeHistoryRepository : ICRUDRepository<History, long>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <param name="status"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<int> CountAsync(int? envelopeId = null, string? userReference = null, EnvelopeStatus? status = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <param name="status"></param>
|
|
||||||
/// <param name="withSender"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<History>> ReadAsync(int? envelopeId = null, string? userReference = null, EnvelopeStatus? status = null, bool withSender = false, bool withReceiver = false);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeReceiverReadOnlyRepository : ICRUDRepository<EnvelopeReceiverReadOnly, long>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeReceiverRepository : ICRUDRepository<EnvelopeReceiver, (int Envelope, int Receiver)>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<EnvelopeReceiver>> ReadByUuidAsync(string uuid, bool withEnvelope = true, bool withReceiver = false, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<EnvelopeReceiver>> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EnvelopeReceiver?> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string?> ReadAccessCodeAsync(string uuid, string signature, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<int> CountAsync(string uuid, string signature);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="receiverId"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EnvelopeReceiver?> ReadByIdAsync(int envelopeId, int receiverId, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="receiverId"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string?> ReadAccessCodeByIdAsync(int envelopeId, int receiverId, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="username"></param>
|
|
||||||
/// <param name="min_status"></param>
|
|
||||||
/// <param name="max_status"></param>
|
|
||||||
/// <param name="ignore_statuses"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<EnvelopeReceiver>> ReadByUsernameAsync(string username, EnvelopeStatus? min_status = null, EnvelopeStatus? max_status = null, params EnvelopeStatus[] ignore_statuses);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="email"></param>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EnvelopeReceiver?> ReadLastByReceiverAsync(string? email = null, int? id = null, string? signature = null);
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeRepository : ICRUDRepository<Envelope, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="documents"></param>
|
|
||||||
/// <param name="history"></param>
|
|
||||||
/// <param name="documentReceiverElement"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<Envelope>> ReadAllWithAsync(bool documents = false, bool history = false, bool documentReceiverElement = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="withDocuments"></param>
|
|
||||||
/// <param name="withHistory"></param>
|
|
||||||
/// <param name="withDocumentReceiverElement"></param>
|
|
||||||
/// <param name="withUser"></param>
|
|
||||||
/// <param name="withAll"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Envelope?> ReadByUuidAsync(string uuid, bool withDocuments = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withUser = false, bool withAll = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId"></param>
|
|
||||||
/// <param name="min_status"></param>
|
|
||||||
/// <param name="max_status"></param>
|
|
||||||
/// <param name="ignore_statuses"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<Envelope>> ReadByUserAsync(int userId, EnvelopeStatus? min_status = null, EnvelopeStatus? max_status = null, params EnvelopeStatus[] ignore_statuses);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IEnvelopeTypeRepository : ICRUDRepository<EnvelopeType, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use IRepository")]
|
|
||||||
public interface IReceiverRepository : ICRUDRepository<Receiver, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="emailAddress"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Receiver?> ReadByAsync(string? emailAddress = null, string? signature = null);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface IDocumentExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="base64"></param>
|
|
||||||
/// <param name="envelope_uuid"></param>
|
|
||||||
/// <param name="cancellation"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Document> CreateDocumentAsync(string base64, string envelope_uuid, CancellationToken cancellation = default);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface IEnvelopeExecutor : ISQLExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId"></param>
|
|
||||||
/// <param name="title"></param>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="tfaEnabled"></param>
|
|
||||||
/// <param name="cancellation"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Envelope> CreateEnvelopeAsync(int userId, string title = "", string message = "", bool tfaEnabled = false, CancellationToken cancellation = default);
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface IEnvelopeReceiverExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelope_uuid"></param>
|
|
||||||
/// <param name="emailAddress"></param>
|
|
||||||
/// <param name="salutation"></param>
|
|
||||||
/// <param name="phone"></param>
|
|
||||||
/// <param name="cancellation"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<EnvelopeReceiver?> AddEnvelopeReceiverAsync(string envelope_uuid, string emailAddress, string? salutation = null, string? phone = null, CancellationToken cancellation = default);
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides methods for executing common queries on a given entity type.
|
|
||||||
/// This interface abstracts away the direct usage of ORM libraries (such as Entity Framework) for querying data
|
|
||||||
/// and provides asynchronous and synchronous operations for querying a collection or single entity.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The type of the entity being queried.</typeparam>
|
|
||||||
public interface IQuery<TEntity>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves the first entity or a default value if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns>
|
|
||||||
public Task<TEntity?> FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves a single entity or a default value if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns>
|
|
||||||
public Task<TEntity?> SingleOrDefaultAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves a list of entities.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the list of entities.</returns>
|
|
||||||
public Task<IEnumerable<TEntity>> ToListAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves the first entity. Throws an exception if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the first entity.</returns>
|
|
||||||
public Task<TEntity> FirstAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asynchronously retrieves a single entity. Throws an exception if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the single entity.</returns>
|
|
||||||
public Task<TEntity> SingleAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves the first entity or a default value if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The first entity or a default value.</returns>
|
|
||||||
public TEntity? FirstOrDefault();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves a single entity or a default value if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The single entity or a default value.</returns>
|
|
||||||
public TEntity? SingleOrDefault();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves a list of entities.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The list of entities.</returns>
|
|
||||||
public IEnumerable<TEntity> ToList();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves the first entity. Throws an exception if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The first entity.</returns>
|
|
||||||
public TEntity First();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronously retrieves a single entity. Throws an exception if no entity is found.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The single entity.</returns>
|
|
||||||
public TEntity Single();
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a raw SQL query contract.
|
|
||||||
/// </summary>
|
|
||||||
public interface ISQL
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the raw SQL query string.
|
|
||||||
/// </summary>
|
|
||||||
string Raw { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a typed SQL query contract for a specific entity.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The type of the entity associated with the SQL query.</typeparam>
|
|
||||||
public interface ISQL<TEntity> : ISQL
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines methods for executing raw SQL queries or custom SQL query classes and returning query executors for further operations.
|
|
||||||
/// Provides abstraction for raw SQL execution as well as mapping the results to <typeparamref name="TEntity"/> objects.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The entity type to which the SQL query results will be mapped.</typeparam>
|
|
||||||
public interface ISQLExecutor<TEntity>: ISQLExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Executes a raw SQL query and returns an <see cref="IQuery{TEntity}"/> for further querying operations on the result.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sql">The raw SQL query to execute.</param>
|
|
||||||
/// <param name="cancellation">Optional cancellation token for the operation.</param>
|
|
||||||
/// <param name="parameters">Optional parameters for the SQL query.</param>
|
|
||||||
/// <returns>An <see cref="IQuery{TEntity}"/> instance for further query operations on the result.</returns>
|
|
||||||
IQuery<TEntity> Execute(string sql, CancellationToken cancellation = default, params object[] parameters);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Executes a custom SQL query defined by a class that implements <see cref="ISQL{TEntity}"/> and returns an <see cref="IQuery{TEntity}"/> for further querying operations on the result.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TSQL">The type of the custom SQL query class implementing <see cref="ISQL{TEntity}"/>.</typeparam>
|
|
||||||
/// <param name="cancellation">Optional cancellation token for the operation.</param>
|
|
||||||
/// <param name="parameters">Optional parameters for the SQL query.</param>
|
|
||||||
/// <returns>An <see cref="IQuery{TEntity}"/> instance for further query operations on the result.</returns>
|
|
||||||
IQuery<TEntity> Execute<TSQL>(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL<TEntity>;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface ISQLExecutor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Executes a raw SQL query and returns an <see cref="IQuery{TEntity}"/> for further querying operations on the result.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The entity type to which the SQL query results will be mapped.</typeparam>
|
|
||||||
/// <param name="sql">The raw SQL query to execute.</param>
|
|
||||||
/// <param name="parameters">Parameters for the SQL query.</param>
|
|
||||||
/// <param name="cancellation">Optional cancellation token for the operation.</param>
|
|
||||||
/// <returns>An <see cref="IQuery{TEntity}"/> instance for further query operations on the result.</returns>
|
|
||||||
Task<IEnumerable<TEntity>> Execute<TEntity>(string sql, DynamicParameters parameters, CancellationToken cancellation = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Executes a custom SQL query defined by a class that implements <see cref="ISQL{TEntity}"/> and returns an <see cref="IQuery{TEntity}"/> for further querying operations on the result.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntity">The entity type to which the SQL query results will be mapped.</typeparam>
|
|
||||||
/// <typeparam name="TSQL">The type of the custom SQL query class implementing <see cref="ISQL{TEntity}"/>.</typeparam>
|
|
||||||
/// <param name="parameters">Parameters for the SQL query.</param>
|
|
||||||
/// <param name="cancellation">Optional cancellation token for the operation.</param>
|
|
||||||
/// <returns>An <see cref="IQuery{TEntity}"/> instance for further query operations on the result.</returns>
|
|
||||||
Task<IEnumerable<TEntity>> Execute<TEntity, TSQL>(DynamicParameters parameters, CancellationToken cancellation = default) where TSQL : ISQL;
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using OtpNet;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface IAuthenticator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
string GenerateCode(int length);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
string GenerateTotpSecretKey(int? length = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userEmail"></param>
|
|
||||||
/// <param name="secretKey"></param>
|
|
||||||
/// <param name="issuer"></param>
|
|
||||||
/// <param name="totpUrlFormat"></param>
|
|
||||||
/// <param name="pixelsPerModule"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
byte[] GenerateTotpQrCode(string userEmail, string secretKey, string? issuer = null, string? totpUrlFormat = null, int? pixelsPerModule = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userEmail"></param>
|
|
||||||
/// <param name="length"></param>
|
|
||||||
/// <param name="issuer"></param>
|
|
||||||
/// <param name="totpUrlFormat"></param>
|
|
||||||
/// <param name="pixelsPerModule"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
byte[] GenerateTotpQrCode(string userEmail, int? length = null, string? issuer = null, string? totpUrlFormat = null, int? pixelsPerModule = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="secretKey"></param>
|
|
||||||
/// <param name="step"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
string GenerateTotp(string secretKey, int step = 30);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="totpCode"></param>
|
|
||||||
/// <param name="secretKey"></param>
|
|
||||||
/// <param name="step"></param>
|
|
||||||
/// <param name="window"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
bool VerifyTotp(string totpCode, string secretKey, int step = 30, VerificationWindow? window = null);
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IConfigService : IReadService<ConfigDto, Config, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<ConfigDto>> ReadFirstAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<ConfigDto> ReadDefaultAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string> ReadDefaultSignatureHost();
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IDocumentReceiverElementService : IBasicCRUDService<DocReceiverElementDto, DocReceiverElement, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IDocumentStatusService : IBasicCRUDService<DocumentStatusDto, DocumentStatus, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IEmailTemplateService : IBasicCRUDService<EmailTemplateDto, EmailTemplate, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<EmailTemplateDto>> ReadByNameAsync(EmailTemplateType type);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IEnvelopeDocumentService : IBasicCRUDService<DocumentDto, Document, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.History;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IEnvelopeHistoryService : ICRUDService<HistoryCreateDto, HistoryDto, History, long>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <param name="status"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<int> CountAsync(int? envelopeId = null, string? userReference = null, EnvelopeStatus? status = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<bool> AccessCodeAlreadyRequested(int envelopeId, string userReference);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<bool> IsSigned(int envelopeId, string userReference);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<bool> IsRejected(int envelopeId, string? userReference = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <param name="referenceType"></param>
|
|
||||||
/// <param name="status"></param>
|
|
||||||
/// <param name="withSender"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<HistoryDto>> ReadAsync(int? envelopeId = null, string? userReference = null, ReferenceType? referenceType = null, EnvelopeStatus? status = null, bool withSender = false, bool withReceiver = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<HistoryDto>> ReadRejectedAsync(int envelopeId, string? userReference = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IEnumerable<ReceiverDto>> ReadRejectingReceivers(int envelopeId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="userReference"></param>
|
|
||||||
/// <param name="status"></param>
|
|
||||||
/// <param name="comment"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<long>> RecordAsync(int envelopeId, string userReference, EnvelopeStatus status, string? comment = null);
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Contracts;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IEnvelopeMailService : IEmailOutService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverDto"></param>
|
|
||||||
/// <param name="tempType"></param>
|
|
||||||
/// <param name="optionalPlaceholders"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<int>> SendAsync(EnvelopeReceiverDto envelopeReceiverDto, EmailTemplateType tempType, Dictionary<string, object>? optionalPlaceholders = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dto"></param>
|
|
||||||
/// <param name="optionalPlaceholders"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<int>> SendAsync(EnvelopeReceiverReadOnlyDto dto, Dictionary<string, object>? optionalPlaceholders = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverDto"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto envelopeReceiverDto);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IEnvelopeReceiverReadOnlyService : ICRUDService<EnvelopeReceiverReadOnlyCreateDto, EnvelopeReceiverReadOnlyDto, EnvelopeReceiverReadOnly, long>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
using CommandDotNet;
|
|
||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
using EnvelopeGenerator.Application.Envelopes;
|
|
||||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
|
||||||
using EnvelopeGenerator.Application.Receivers.Queries;
|
|
||||||
using EnvelopeGenerator.Domain;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IEnvelopeReceiverService : IBasicCRUDService<EnvelopeReceiverDto, EnvelopeReceiver, (int Envelope, int Receiver)>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
|
|
||||||
Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUuidAsync(string uuid, bool withEnvelope = true, bool withReceiver = false, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<IEnumerable<string?>>> ReadAccessCodeByUuidAsync(string uuid, bool withEnvelope = false, bool withReceiver = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadBySignatureAsync(string signature, bool withEnvelope = false, bool withReceiver = true, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<EnvelopeReceiverDto>> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<EnvelopeReceiverSecretDto>> ReadWithSecretByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverId"></param>
|
|
||||||
/// <param name="withEnvelope"></param>
|
|
||||||
/// <param name="withReceiver"></param>
|
|
||||||
/// <param name="readOnly"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<EnvelopeReceiverDto>> ReadByEnvelopeReceiverIdAsync(string envelopeReceiverId, bool withEnvelope = true, bool withReceiver = true, bool readOnly = true);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeId"></param>
|
|
||||||
/// <param name="receiverId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<string>> ReadAccessCodeByIdAsync(int envelopeId, int receiverId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="accessCode"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<bool>> VerifyAccessCodeAsync(string uuid, string signature, string accessCode);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverId"></param>
|
|
||||||
/// <param name="accessCode"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Command("verify-access-code-async-by-id")]
|
|
||||||
Task<DataResult<bool>> VerifyAccessCodeAsync(string envelopeReceiverId, string accessCode);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<bool>> IsExisting(string envelopeReceiverId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="username"></param>
|
|
||||||
/// <param name="min_status"></param>
|
|
||||||
/// <param name="max_status"></param>
|
|
||||||
/// <param name="envelopeQuery"></param>
|
|
||||||
/// <param name="receiverQuery"></param>
|
|
||||||
/// <param name="ignore_statuses"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<IEnumerable<EnvelopeReceiverDto>>> ReadByUsernameAsync(string username, EnvelopeStatus? min_status = null, EnvelopeStatus? max_status = null, ReadEnvelopeQuery? envelopeQuery = null, ReadReceiverQuery? receiverQuery = null, params EnvelopeStatus[] ignore_statuses);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mail"></param>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<string>> ReadLastUsedReceiverNameByMailAsync(string? mail = null, int? id = null, string? signature = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverId"></param>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<SmsResponse>> SendSmsAsync(string envelopeReceiverId, string message);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<IEnumerable<EnvelopeReceiverSecretDto>>> ReadWithSecretByUuidAsync(string uuid);
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IEnvelopeService : IBasicCRUDService<EnvelopeDto, Envelope, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="documents"></param>
|
|
||||||
/// <param name="history"></param>
|
|
||||||
/// <param name="documentReceiverElement"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<IEnumerable<EnvelopeDto>>> ReadAllWithAsync(bool documents = false, bool history = false, bool documentReceiverElement = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="withDocuments"></param>
|
|
||||||
/// <param name="withHistory"></param>
|
|
||||||
/// <param name="withDocumentReceiverElement"></param>
|
|
||||||
/// <param name="withUser"></param>
|
|
||||||
/// <param name="withAll"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<EnvelopeDto>> ReadByUuidAsync(string uuid, bool withDocuments = false, bool withHistory = false, bool withDocumentReceiverElement = false, bool withUser = false, bool withAll = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId"></param>
|
|
||||||
/// <param name="min_status"></param>
|
|
||||||
/// <param name="max_status"></param>
|
|
||||||
/// <param name="ignore_statuses"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<IEnumerable<EnvelopeDto>>> ReadByUserAsync(int userId, EnvelopeStatus? min_status = null, EnvelopeStatus? max_status = null, params EnvelopeStatus[] ignore_statuses);
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface IEnvelopeSmsHandler
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// If expiration is passed then, sends sms and returns smsResponse and up-to-date expiration; otherwise send expiration.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="er_secret"></param>
|
|
||||||
/// <param name="cToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<(SmsResponse? SmsResponse, DateTime Expiration)> SendTotpAsync(EnvelopeReceiverSecretDto er_secret, CancellationToken cToken = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="totpCode"></param>
|
|
||||||
/// <param name="secretKey"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
bool VerifyTotp(string totpCode, string secretKey);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IEnvelopeTypeService : IBasicCRUDService<EnvelopeTypeDto, EnvelopeType, int>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application;
|
|
||||||
using DigitalData.Core.Abstraction.Application.DTO;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|
||||||
using EnvelopeGenerator.Application.Receivers.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use MediatR")]
|
|
||||||
public interface IReceiverService : ICRUDService<CreateReceiverCommand, ReceiverDto, Receiver, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="emailAddress"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<DataResult<ReceiverDto>> ReadByAsync(string? emailAddress = null, string? signature = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="emailAddress"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Result> DeleteByAsync(string? emailAddress = null, string? signature = null);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.Messaging;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
//TODO: move to DigitalData.Core
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public interface ISmsSender
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
string ServiceProvider { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="recipient"></param>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<SmsResponse> SendSmsAsync(string recipient, string message);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manages per-user UI preferences (theme, dark mode, grid layouts).
|
|
||||||
/// Preferences are stored in the distributed cache (dbo.TBDD_CACHE) as JSON.
|
|
||||||
/// Cache key schema: "signflow.user-preferences:{username}"
|
|
||||||
/// </summary>
|
|
||||||
public interface IUserPreferencesService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves preferences for the given user.
|
|
||||||
/// Always returns a valid object — never throws, never returns null.
|
|
||||||
/// Returns defaults if no entry exists in cache.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
Task<UserPreferencesDto> GetOrCreateAsync(string id, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Persists the given preferences for the user in the distributed cache.
|
|
||||||
/// Failures are non-fatal and silently ignored.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <param name="preferences">The preferences to persist.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
Task SaveAsync(string id, UserPreferencesDto preferences, CancellationToken ct = default);
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
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;
|
|
||||||
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the AutoMapper profile configuration for mapping between
|
|
||||||
/// domain entities and data transfer objects (DTOs) used within the EnvelopeGenerator application.
|
|
||||||
/// </summary>
|
|
||||||
public class MappingProfile : Profile
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="MappingProfile"/> class.
|
|
||||||
/// Configures the mappings between entities and DTOs used throughout the application.
|
|
||||||
/// </summary>
|
|
||||||
public MappingProfile()
|
|
||||||
{
|
|
||||||
// Entity to DTO mappings
|
|
||||||
CreateMap<Config, ConfigDto>();
|
|
||||||
CreateMap<DocReceiverElement, DocReceiverElementDto>();
|
|
||||||
CreateMap<DocumentStatus, DocumentStatusDto>();
|
|
||||||
CreateMap<EmailTemplate, EmailTemplateDto>();
|
|
||||||
CreateMap<Envelope, EnvelopeDto>()
|
|
||||||
.ForMember(dest => dest.EnvelopeTypeTitle,
|
|
||||||
opt => opt.MapFrom(src => src.Type != null ? src.Type.Title : null));
|
|
||||||
CreateMap<Document, DocumentDto>();
|
|
||||||
CreateMap<History, HistoryDto>().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen));
|
|
||||||
CreateMap<History, HistoryCreateDto>().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen));
|
|
||||||
CreateMap<EnvelopeReceiver, EnvelopeReceiverDto>();
|
|
||||||
CreateMap<EnvelopeReceiver, EnvelopeReceiverSecretDto>();
|
|
||||||
CreateMap<EnvelopeType, EnvelopeTypeDto>();
|
|
||||||
CreateMap<Receiver, ReceiverDto>();
|
|
||||||
CreateMap<EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
|
|
||||||
CreateMap<ElementAnnotation, AnnotationDto>();
|
|
||||||
|
|
||||||
// DTO to Entity mappings
|
|
||||||
CreateMap<ConfigDto, Config>();
|
|
||||||
CreateMap<DocReceiverElementDto, DocReceiverElement>();
|
|
||||||
CreateMap<Signature, DocReceiverElement>()
|
|
||||||
.ForMember(dest => dest.Ink, opt => opt.MapFrom(src => src.DataUrl.MapDataUrlToRequiredBytes()))
|
|
||||||
.MapChangedWhen();
|
|
||||||
CreateMap<DocumentStatusDto, DocumentStatus>();
|
|
||||||
CreateMap<EmailTemplateDto, EmailTemplate>();
|
|
||||||
CreateMap<EnvelopeDto, Envelope>();
|
|
||||||
CreateMap<DocumentDto, Document>();
|
|
||||||
CreateMap<HistoryDto, History>().ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(src => src.ActionDate));
|
|
||||||
CreateMap<HistoryCreateDto, History>().ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(src => src.ActionDate));
|
|
||||||
CreateMap<EnvelopeReceiverDto, EnvelopeReceiver>();
|
|
||||||
CreateMap<EnvelopeTypeDto, EnvelopeType>();
|
|
||||||
CreateMap<ReceiverDto, Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore());
|
|
||||||
CreateMap<EnvelopeReceiverReadOnlyCreateDto, EnvelopeReceiverReadOnly>();
|
|
||||||
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, EnvelopeReceiverReadOnly>();
|
|
||||||
CreateMap<AnnotationCreateDto, ElementAnnotation>()
|
|
||||||
.MapAddedWhen();
|
|
||||||
|
|
||||||
// Messaging mappings
|
|
||||||
// for GTX messaging
|
|
||||||
CreateMap<GtxMessagingResponse, SmsResponse>()
|
|
||||||
.ConstructUsing(gtxRes => gtxRes.Ok()
|
|
||||||
? new SmsResponse() { Ok = true }
|
|
||||||
: new SmsResponse() { Ok = false, Errors = gtxRes });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Notification raised when a document is signed by a receiver.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
|
||||||
public record DocSignedNotification : INotification, ISendMailNotification
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The envelope receiver information.
|
|
||||||
/// </summary>
|
|
||||||
public required EnvelopeReceiverDto EnvelopeReceiver { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The PSPDFKit annotation data.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("The PSPDFKit library is deprecated.")]
|
|
||||||
public PsPdfKitAnnotation? PsPdfKitAnnotation { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the email template type.
|
|
||||||
/// </summary>
|
|
||||||
public EmailTemplateType TemplateType => EmailTemplateType.DocumentSigned;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the email address of the receiver.
|
|
||||||
/// </summary>
|
|
||||||
public string EmailAddress => EnvelopeReceiver.Receiver?.EmailAddress
|
|
||||||
?? throw new InvalidOperationException($"Receiver is null." +
|
|
||||||
$"DocSignedNotification:\n{this.ToJson(Format.Json.ForDiagnostics)}");
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("The PSPDFKit library is deprecated.")]
|
|
||||||
public class AnnotationHandler : INotificationHandler<DocSignedNotification>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
private readonly IRepository<ElementAnnotation> _repo;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public AnnotationHandler(IRepository<ElementAnnotation> repository)
|
|
||||||
{
|
|
||||||
_repo = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="notification"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
if (notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot)
|
|
||||||
await _repo.CreateAsync(annot.Structured, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.DocStatus.Commands;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
|
||||||
public class DocStatusHandler : INotificationHandler<DocSignedNotification>
|
|
||||||
{
|
|
||||||
private const string BlankAnnotationJson = "{}";
|
|
||||||
|
|
||||||
private readonly ISender _sender;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
public DocStatusHandler(ISender sender)
|
|
||||||
{
|
|
||||||
_sender = sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="notification"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
|
||||||
public Task Handle(DocSignedNotification notification, CancellationToken cancel) => _sender.Send(new CreateDocStatusCommand()
|
|
||||||
{
|
|
||||||
EnvelopeId = notification.EnvelopeReceiver.EnvelopeId,
|
|
||||||
ReceiverId = notification.EnvelopeReceiver.ReceiverId,
|
|
||||||
Value = notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot
|
|
||||||
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
|
|
||||||
: BlankAnnotationJson
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.Histories.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
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.EnvelopeReceiver.Receiver is null)
|
|
||||||
throw new InvalidOperationException($"Receiver information is missing in the notification. DocSignedNotification:\n {notification.ToJson(Format.Json.ForDiagnostics)}");
|
|
||||||
|
|
||||||
await _sender.Send(new CreateHistoryCommand()
|
|
||||||
{
|
|
||||||
EnvelopeId = notification.EnvelopeReceiver.EnvelopeId,
|
|
||||||
UserReference = notification.EnvelopeReceiver.Receiver.EmailAddress,
|
|
||||||
Status = EnvelopeStatus.DocumentSigned,
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
|
||||||
using EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using EnvelopeGenerator.Domain.Interfaces;
|
|
||||||
|
|
||||||
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.EnvelopeReceiver.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.EnvelopeReceiver.Name ?? string.Empty },
|
|
||||||
{ "[DOCUMENT_TITLE]", notification.EnvelopeReceiver.Envelope?.Title ?? string.Empty },
|
|
||||||
};
|
|
||||||
|
|
||||||
if (notification.EnvelopeReceiver.Envelope.IsReadAndConfirm())
|
|
||||||
{
|
|
||||||
placeHolders["[SIGNATURE_TYPE]"] = "Lesen und bestätigen";
|
|
||||||
placeHolders["[DOCUMENT_PROCESS]"] = string.Empty;
|
|
||||||
placeHolders["[FINAL_STATUS]"] = "Lesebestätigung";
|
|
||||||
placeHolders["[FINAL_ACTION]"] = "Empfänger bestätigt";
|
|
||||||
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Empfänger abgelehnt!";
|
|
||||||
placeHolders["[RECEIVER_ACTION]"] = "bestätigt";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
placeHolders["[SIGNATURE_TYPE]"] = "Signieren";
|
|
||||||
placeHolders["[DOCUMENT_PROCESS]"] = " und elektronisch unterschreiben";
|
|
||||||
placeHolders["[FINAL_STATUS]"] = "Signatur";
|
|
||||||
placeHolders["[FINAL_ACTION]"] = "Vertragspartner unterzeichnet";
|
|
||||||
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.";
|
|
||||||
placeHolders["[RECEIVER_ACTION]"] = "unterschrieben";
|
|
||||||
}
|
|
||||||
|
|
||||||
return placeHolders;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class RemoveAnnotationHandler : INotificationHandler<RemoveSignatureNotification>
|
|
||||||
{
|
|
||||||
private readonly IRepository<ElementAnnotation> _repo;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public RemoveAnnotationHandler(IRepository<ElementAnnotation> repository)
|
|
||||||
{
|
|
||||||
_repo = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="notification"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
notification.ThrowIfHasNoFilter();
|
|
||||||
return _repo.DeleteAsync(annots =>
|
|
||||||
{
|
|
||||||
// envelope ID filter
|
|
||||||
if (notification.EnvelopeId is int envelopeId)
|
|
||||||
annots = annots.Where(annot => annot.Element!.Document.EnvelopeId == envelopeId);
|
|
||||||
|
|
||||||
// envelope UUID filter
|
|
||||||
if (notification.EnvelopeUuid is string envelopeUuid)
|
|
||||||
annots = annots.Where(annot => annot.Element!.Document.Envelope!.Uuid == envelopeUuid);
|
|
||||||
|
|
||||||
// receiver ID
|
|
||||||
if (notification.ReceiverId is int receiverId)
|
|
||||||
annots = annots.Where(annot => annot.Element!.ReceiverId == receiverId);
|
|
||||||
|
|
||||||
// receiver signature
|
|
||||||
if (notification.ReceiverSignature is string receiverSignature)
|
|
||||||
annots = annots.Where(annot => annot.Element!.Receiver!.Signature == receiverSignature);
|
|
||||||
|
|
||||||
return annots;
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
using AngleSharp.Html;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class RemoveDocStatusHandler : INotificationHandler<RemoveSignatureNotification>
|
|
||||||
{
|
|
||||||
private readonly IRepository<Domain.Entities.DocumentStatus> _repo;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public RemoveDocStatusHandler(IRepository<Domain.Entities.DocumentStatus> repository)
|
|
||||||
{
|
|
||||||
_repo = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="notification"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
notification.ThrowIfHasNoFilter();
|
|
||||||
return _repo.DeleteAsync(statuses =>
|
|
||||||
{
|
|
||||||
// envelope ID filter
|
|
||||||
if (notification.EnvelopeId is int envelopeId)
|
|
||||||
statuses = statuses.Where(status => status.EnvelopeId == envelopeId);
|
|
||||||
|
|
||||||
// envelope UUID filter
|
|
||||||
if (notification.EnvelopeUuid is string envelopeUuid)
|
|
||||||
statuses = statuses.Where(status => status.Envelope!.Uuid == envelopeUuid);
|
|
||||||
|
|
||||||
// receiver Id filter
|
|
||||||
if (notification.ReceiverId is int receiverId)
|
|
||||||
statuses = statuses.Where(status => status.ReceiverId == receiverId);
|
|
||||||
|
|
||||||
// receiver signature filter
|
|
||||||
if (notification.ReceiverSignature is string receiverSignature)
|
|
||||||
statuses = statuses.Where(status => status.Receiver!.Signature == receiverSignature);
|
|
||||||
|
|
||||||
return statuses;
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class RemoveHistoryHandler : INotificationHandler<RemoveSignatureNotification>
|
|
||||||
{
|
|
||||||
private readonly IRepository<Domain.Entities.History> _repo;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public RemoveHistoryHandler(IRepository<Domain.Entities.History> repository)
|
|
||||||
{
|
|
||||||
_repo = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="notification"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
notification.ThrowIfHasNoFilter();
|
|
||||||
return _repo.DeleteAsync(hists =>
|
|
||||||
{
|
|
||||||
hists = hists.Where(hist => hist.Status == EnvelopeStatus.DocumentSigned);
|
|
||||||
|
|
||||||
// envelope ID filter
|
|
||||||
if (notification.EnvelopeId is int envelopeId)
|
|
||||||
hists = hists.Where(hist => hist.EnvelopeId == envelopeId);
|
|
||||||
|
|
||||||
// envelope UUID filter
|
|
||||||
if (notification.EnvelopeUuid is string envelopeUuid)
|
|
||||||
hists = hists.Where(hist => hist.Envelope!.Uuid == envelopeUuid);
|
|
||||||
|
|
||||||
// receiver ID filter
|
|
||||||
if (notification.ReceiverId is int receiverId)
|
|
||||||
hists = hists.Where(hist => hist.Receiver!.Id == receiverId);
|
|
||||||
|
|
||||||
// receiver signature filter
|
|
||||||
if (notification.ReceiverSignature is string receiverSignature)
|
|
||||||
hists = hists.Where(hist => hist.Receiver!.Signature == receiverSignature);
|
|
||||||
|
|
||||||
return hists;
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="EnvelopeId"></param>
|
|
||||||
/// <param name="ReceiverId"></param>
|
|
||||||
/// <param name="EnvelopeUuid"></param>
|
|
||||||
/// <param name="ReceiverSignature"></param>
|
|
||||||
public record RemoveSignatureNotification(
|
|
||||||
int? EnvelopeId = null,
|
|
||||||
int? ReceiverId = null,
|
|
||||||
string? EnvelopeUuid = null,
|
|
||||||
string? ReceiverSignature = null
|
|
||||||
) : INotification
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool HasFilter =>
|
|
||||||
EnvelopeId is not null
|
|
||||||
|| ReceiverId is not null
|
|
||||||
|| EnvelopeUuid is not null
|
|
||||||
|| ReceiverSignature is not null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="InvalidOperationException"></exception>
|
|
||||||
public void ThrowIfHasNoFilter()
|
|
||||||
{
|
|
||||||
if (!HasFilter)
|
|
||||||
throw new InvalidOperationException("At least one filter parameter must be provided.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
|
||||||
using EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
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
|
|
||||||
.Where(x => x.Name == notification.TemplateType.ToString())
|
|
||||||
.SingleOrDefaultAsync(cancel)
|
|
||||||
?? throw new InvalidOperationException($"Receiver information is missing in the notification." +
|
|
||||||
$"{typeof(TNotification)}:\n {notification.ToJson(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 = temp.Body,
|
|
||||||
EmailSubj = temp.Subject,
|
|
||||||
AddedWhen = DateTime.Now,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
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; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks whether any of the specified query criteria have a value.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This property returns <c>true</c> if at least one of the fields
|
|
||||||
/// <see cref="Id"/>, <see cref="EmailAddress"/>, or <see cref="Signature"/> is not null.
|
|
||||||
/// <para>Usage example: The query can be executed only if at least one criterion is specified.</para>
|
|
||||||
/// </remarks>
|
|
||||||
[NotMapped]
|
|
||||||
public virtual bool HasAnyCriteria => Id is not null || EmailAddress is not null || Signature is not null;
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.SQL;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class DocumentCreateReadSQL : ISQL<Document>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base64, OUT_UID
|
|
||||||
/// </summary>
|
|
||||||
public string Raw => @"
|
|
||||||
DECLARE @BYTE_DATA1 as VARBINARY(MAX)
|
|
||||||
SET @BYTE_DATA1 = @ByteData
|
|
||||||
DECLARE @OUT_DOCID int
|
|
||||||
|
|
||||||
EXEC [dbo].[PRSIG_API_ADD_DOC]
|
|
||||||
{0},
|
|
||||||
@BYTE_DATA1,
|
|
||||||
@OUT_DOCID OUTPUT
|
|
||||||
|
|
||||||
SELECT TOP(1) *
|
|
||||||
FROM [dbo].[TBSIG_ENVELOPE_DOCUMENT]
|
|
||||||
WHERE [GUID] = @OUT_DOCID
|
|
||||||
";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="base64"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DynamicParameters CreateParmas(string base64)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
byte[] byteData = Convert.FromBase64String(base64);
|
|
||||||
parameters.Add("ByteData", byteData, System.Data.DbType.Binary);
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
catch(FormatException ex)
|
|
||||||
{
|
|
||||||
throw new BadRequestException(ex.Message.Replace("input", "dataAsBase64"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.SQL;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class EnvelopeCreateReadSQL : ISQL<Envelope>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// USER_ID, TITLE, TFAEnabled, MESSAGE
|
|
||||||
/// </summary>
|
|
||||||
public string Raw => @"
|
|
||||||
DECLARE @OUT_UID varchar(36);
|
|
||||||
|
|
||||||
EXEC [dbo].[PRSIG_API_CREATE_ENVELOPE]
|
|
||||||
{0},
|
|
||||||
{1},
|
|
||||||
{2},
|
|
||||||
{3},
|
|
||||||
@OUT_UID OUTPUT;
|
|
||||||
|
|
||||||
SELECT TOP(1) *
|
|
||||||
FROM [dbo].[TBSIG_ENVELOPE]
|
|
||||||
WHERE [ENVELOPE_UUID] = @OUT_UID;
|
|
||||||
";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId"></param>
|
|
||||||
/// <param name="title"></param>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="tfaEnabled"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DynamicParameters CreateParams(int userId, string title = "", string message = "", bool tfaEnabled = false)
|
|
||||||
{
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
parameters.Add("@UserId", userId);
|
|
||||||
parameters.Add("@Title", title);
|
|
||||||
parameters.Add("@TfaEnabled", tfaEnabled ? 1 : 0);
|
|
||||||
parameters.Add("@Message", message);
|
|
||||||
parameters.Add("@OutUid", dbType: DbType.String, size: 36, direction: ParameterDirection.Output);
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using Dapper;
|
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.SQL;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class EnvelopeReceiverAddReadSQL : ISQL<Envelope>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// ENV_UID, EMAIL_ADRESS, SALUTATION, PHONE,
|
|
||||||
/// </summary>
|
|
||||||
public string Raw => @"
|
|
||||||
DECLARE @OUT_RECEIVER_ID bigint
|
|
||||||
|
|
||||||
EXEC [dbo].[PRSIG_API_CREATE_RECEIVER]
|
|
||||||
{0},
|
|
||||||
{1},
|
|
||||||
{2},
|
|
||||||
{3},
|
|
||||||
@OUT_RECEIVER_ID OUTPUT
|
|
||||||
|
|
||||||
SELECT TOP(1) [ENVELOPE_ID] As EnvelopeId, [RECEIVER_ID] As ReceiverId
|
|
||||||
FROM [dbo].[TBSIG_ENVELOPE_RECEIVER]
|
|
||||||
WHERE [GUID] = @OUT_RECEIVER_ID;
|
|
||||||
";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelope_uuid"></param>
|
|
||||||
/// <param name="emailAddress"></param>
|
|
||||||
/// <param name="salutation"></param>
|
|
||||||
/// <param name="phone"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static DynamicParameters CreateParameters(string envelope_uuid, string emailAddress, string? salutation = null, string? phone = null)
|
|
||||||
{
|
|
||||||
var parameters = new DynamicParameters();
|
|
||||||
parameters.Add("@ENV_UID", envelope_uuid);
|
|
||||||
parameters.Add("@EMAIL_ADRESS", emailAddress);
|
|
||||||
parameters.Add("@SALUTATION", salutation);
|
|
||||||
parameters.Add("@PHONE", phone);
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.SQL;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extension method for converting objects to SQL parameter strings.
|
|
||||||
/// </summary>
|
|
||||||
public static class ParamsExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a .NET object to its corresponding SQL-safe parameter string.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">The object to convert.</param>
|
|
||||||
/// <returns>A string representing the SQL parameter.</returns>
|
|
||||||
public static string ToSqlParam(this object? obj)
|
|
||||||
{
|
|
||||||
if (obj is null)
|
|
||||||
return "NULL";
|
|
||||||
else if (obj is string strVal)
|
|
||||||
return $"'{strVal}'";
|
|
||||||
else if (obj is bool boolVal)
|
|
||||||
return boolVal ? "1" : "0";
|
|
||||||
else if (obj is double doubleVal)
|
|
||||||
return $"'{doubleVal.ToString(CultureInfo.InvariantCulture)}'";
|
|
||||||
else if (obj is int intVal)
|
|
||||||
return intVal.ToString();
|
|
||||||
else
|
|
||||||
throw new NotSupportedException($"Type '{obj.GetType().FullName}' is not supported for SQL parameter conversion.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Configurations
|
||||||
|
{
|
||||||
|
public class DispatcherConfig
|
||||||
|
{
|
||||||
|
public int SendingProfile { get; init; } = 1;
|
||||||
|
|
||||||
|
public string AddedWho { get; init; } = "DDEnvelopGenerator";
|
||||||
|
|
||||||
|
public int ReminderTypeId { get; init; } = 202377;
|
||||||
|
|
||||||
|
public string EmailAttmt1 { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Configurations
|
||||||
|
{
|
||||||
|
public class MailConfig
|
||||||
|
{
|
||||||
|
public required Dictionary<string, string> Placeholders { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
EnvelopeGenerator.Application/Contracts/IConfigService.cs
Normal file
17
EnvelopeGenerator.Application/Contracts/IConfigService.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using DigitalData.Core.Abstractions.Application;
|
||||||
|
using DigitalData.Core.DTO;
|
||||||
|
using EnvelopeGenerator.Application.DTOs;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Contracts
|
||||||
|
{
|
||||||
|
public interface IConfigService : IReadService<ConfigDto, Config, int>
|
||||||
|
{
|
||||||
|
Task<DataResult<ConfigDto>> ReadFirstAsync();
|
||||||
|
|
||||||
|
Task<ConfigDto> ReadDefaultAsync();
|
||||||
|
|
||||||
|
Task<string> ReadDefaultSignatureHost();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using DigitalData.Core.Abstractions.Application;
|
||||||
|
using EnvelopeGenerator.Application.DTOs;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Contracts
|
||||||
|
{
|
||||||
|
public interface IDocumentReceiverElementService : IBasicCRUDService<DocumentReceiverElementDto, DocumentReceiverElement, int>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user