PdfGeneration

Info

Architectural truth for PdfGeneration in the Documents feature. This leaf module handles the asynchronous and manual generation, storage, and retrieval of PDF files.

Overview

PDF Generation is a critical export feature of MILTON. A completed document (consisting of sections and generated blocks) must be converted into a shareable PDF. This process happens automatically at the end of the DocumentProcessingSaga or can be triggered manually by a user.

Because rendering an entire document to HTML and converting it via an external service (Gotenberg) can be slow, PDF generation is handled as a background message.

Business Logic Intent

The core intent is to cleanly separate the heavy lifting of PDF rendering from the API’s HTTP request-response cycle.

  1. Rendering: The IDocumentRenderService takes the document entity and renders it into a complete HTML string (with embedded assets/styling).
  2. Conversion: The HTML is sent to the Gotenberg container via IPdfStorageService (or similar infrastructure) which returns a PDF file.
  3. Storage: The PDF binary is uploaded to an S3-compatible blob store.
  4. Linking: The S3 object key (PdfStorageKey) is saved to the Document record in milton-db.

Endpoints & Flow

  • GeneratePdfEndpoint: Manual trigger. Enqueues a GeneratePdfCommand to Wolverine and returns a 200 OK to the client immediately.
  • DownloadPdfEndpoint: Synchronous fetch. Given a DocumentId, it retrieves the PdfStorageKey from the database, fetches the binary from S3, and streams it back to the client as an application/pdf download.

Wolverine Handlers

  • GeneratePdfOnCompletionHandler: Consumes GeneratePdfCommand (emitted manually or by the document saga). Orchestrates the render → convert → store process and then updates the Document entity. Finally, it publishes PdfGeneratedEvent so SignalR can notify the client that the PDF is ready.

Sequence: PDF Generation Pipeline

sequenceDiagram
    participant Client
    participant API as API / Saga
    participant Bus as Wolverine
    participant Handler as GeneratePdfHandler
    participant DB as milton-db
    participant Gotenberg as Gotenberg (PDF)
    participant S3 as S3 Storage

    alt Manual Trigger
        Client->>API: POST /documents/{id}/pdf
    else Saga Trigger
        API->>API: DocumentGenerationCompleted
    end
    
    API->>Bus: Publish(GeneratePdfCommand)
    Bus->>Handler: Consume(GeneratePdfCommand)
    
    Handler->>DB: Fetch Document & Blocks
    Handler->>Handler: Render to HTML
    Handler->>Gotenberg: Convert HTML to PDF
    Gotenberg-->>Handler: PDF binary stream
    Handler->>S3: Upload PDF stream
    S3-->>Handler: Return StorageKey
    
    Handler->>DB: Update Document.PdfStorageKey
    Handler->>Bus: Publish(PdfGeneratedEvent)
    Bus-->>Client: (SignalR) PDF Ready Notification

Domain Models

  • Commands/Events: GeneratePdfCommand, PdfGeneratedEvent, PdfGenerationFailedEvent
  • Request Models: DownloadPdfRequest, GeneratePdfRequest

0 items under this folder.