CodamAIDocs
Themafertig

Vom Klick bis zum Bericht

Eine Person lässt sich einen Bericht erzeugen. Jeder Schritt über BFF, Anwendung, CDMS, CIAS und CRMS bis zum fertigen Dokument, eingebettet und als eigener Dienst, als Vorschau, endgültig und flüchtig.

Ausprägungen
CRMS eingebettet (local)CRMS als eigener Dienst (remote, crms-client)PREVIEWFINALTRANSIENTAbbruch an jeder Station

Worum es geht

Ben arbeitet für den Kunden nordbau. Er hat ein Audit abgeschlossen und klickt auf „Prüfbericht erzeugen“. Ein paar Sekunden später hat er ein PDF. Dazwischen arbeiten alle drei Module zusammen, und jedes tut genau eine Sache:

  • CDMS hält die Daten des Audits.
  • CIAS sagt, wer anfragt, für welchen Mandanten und mit welchen Rollen.
  • CRMS macht aus einer Vorlage und Daten ein Dokument und bewahrt es auf, wenn es ein Nachweis ist.

Diese Seite ist eine Abzweigung von Vom Login bis zu den Daten. Anmeldung, BFF und Filterkette laufen genau so wie dort. Hier geht es um das, was danach kommt.

Der ganze Weg

So sieht der Weg aus, wenn CRMS eingebettet in der Anwendung läuft und Ben einen endgültigen Bericht verlangt:

sequenceDiagram
    autonumber
    participant U as Benutzer
    participant F as BFF
    participant FK as Filterkette (CIAS)
    participant A as eigener Code der Anwendung
    participant C as CDMS
    participant R as CRMS
    participant DB as Mandanten-DB
    participant S as Dateispeicher
    U->>F: klickt auf „Prüfbericht erzeugen“
    F->>FK: POST /api/rest/audit/report mit Bearer-Token
    FK->>FK: Token prüfen, Mandant nordbau, Tor, Rollen
    FK->>A: RequestContext gefüllt
    A->>C: liest Audit, Fragen, Antworten
    C->>DB: SELECT (Datenbank von nordbau)
    DB-->>C: Zeilen
    C-->>A: Objekte
    A->>A: baut die Payload, höchstens zwei Ebenen
    A->>R: ReportRenderPort.render(FINAL, audit-report, audit-4711, Payload)
    R->>FK: wer ruft? (aus dem RequestContext)
    FK-->>R: Ben, nordbau, Rollen
    R->>DB: sucht die Vorlage (Ben, nordbau, System)
    R->>R: prüft die Payload
    R->>R: rendert im Pool, siegelt das PDF
    R->>S: legt die verschlüsselten Bytes gesperrt ab
    R->>DB: hängt die Revision an die Kette, Journal
    R-->>A: RenderedDocument mit Revision
    A-->>F: PDF
    F-->>U: Download

Die folgenden Etappen gehen den Weg langsam durch.

Etappe 1: Bis zur Anwendung

Der Browser ruft wie immer eine Route seines BFF auf. Der BFF schickt die Anfrage mit Bens Access-Token an das Backend. Dort läuft sie zuerst durch die Filterkette von CIAS: Token prüfen, Mandant bestimmen, Mandanten-Tor fragen, effektive Rollen bilden. Am Ende steht der RequestContext, der Merkzettel für genau diese Anfrage.

Das ist derselbe Weg wie bei jeder anderen Anfrage, Station für Station unter Vom Login bis zu den Daten. Wird nordbau nicht bedient, endet der Bericht hier mit 403, bevor eine Zeile Code der Anwendung läuft.

Etappe 2: Die Anwendung sammelt die Daten

Eine Route für „Prüfbericht erzeugen“ erzeugt der Generator nicht. Das ist eigener Code der Anwendung: ein eigener Controller, wie die Berichte des Hubs ihn haben, oder ein Hook. Beides sind Spring-Beans, und beide lassen sich den Port von CRMS geben.

Was der eigene Code tut
  1. 1
    Anwendung→CDMS
    liest das Audit mit allem, was der Bericht zeigt: Listen, Gruppen, Fragen, Antworten
    Das läuft im RequestContext von Bens Anfrage. CDMS wählt darum die Datenbank von nordbau.
  2. 2
    Anwendung
    baut daraus die Payload: höchstens zwei Ebenen, an den Enden nur Text, Zahl, Wahrheitswert oder Datum
    Summen, Prozente und Bewertungen rechnet die Anwendung vorher aus. CRMS rechnet nicht und lädt nichts nach.
  3. 3
    Anwendung→CRMS
    ruft ReportRenderPort.render(…) auf, mit Vorlagenschlüssel, Format, Modus und bei FINAL einer Referenz
    Ergebnis: Ab hier ist CRMS dran. Die Anwendung wartet im selben Aufruf auf das Dokument.

So sieht der Aufruf aus:

RenderedDocument document = renderer.render(RenderRequest.finalDocument(
    new TemplateKey("audit-report"),       // template key
    OutputFormat.PDF,
    new DocumentReference("audit-4711"),   // the chain: the audit's ID
    payload));                             // at most two levels

Die Referenz ist die Kennung des Fachobjekts in deiner Anwendung, hier die ID des Audits. CRMS liest nicht hinein, es legt alle endgültigen Fassungen desselben Berichts darunter ab. Wie die Payload aussehen darf, steht unter Die Payload und ihre Prüfung, alle Felder der Anfrage unter Die Render-Anfrage.

Etappe 3: CRMS fragt CIAS, wer anfragt

CRMS liest kein Token. Es fragt CIAS nach dem Aufrufer, und CIAS antwortet aus dem RequestContext, den die Filterkette schon gefüllt hat:

Was CRMS bekommtWoherWofür CRMS es braucht
angemeldet ja oder neinRequestContextohne Person kein Bericht: crms-authentication-required
die Persondie Benutzer-ID aus dem Tokensteht an jedem endgültigen Dokument, findet Bens persönliche Vorlagen
der Mandantder Mandant der Anfrage, hier nordbauwelche Mandantenvorlagen gelten, wem das Dokument gehört
die RollenRealm-Rollen und Rollen des ClientsVorlagen schreiben, freigeben, Dokumente lesen

Rendern braucht keine Rolle. Jede angemeldete Person darf rendern, mit jeder Vorlage, die sie sieht. Die Rollen spielen erst beim Bearbeiten von Vorlagen und beim Lesen gespeicherter Dokumente eine Rolle, siehe Die Rollen.

Etappe 4: Die Vorlage finden

Die Anfrage nennt nur den Schlüssel audit-report. Welche Ausgabe und welche Revision rendert, entscheidet CRMS. Eine Ausgabe ist die Fassung einer Vorlage für einen Scope, also für das System, einen Mandanten oder eine Person. Die genaueste gewinnt:

Wo CRMS nach audit-report sucht
  1. 1
    CRMS→Datenbank
    Hat Ben eine eigene Ausgabe (Scope USER)? Gesucht wird in der Datenbank von nordbau
  2. 2
    CRMS→Datenbank
    Hat nordbau eine Ausgabe (Scope TENANT)? Ebenfalls in der Datenbank von nordbau
  3. 3
    CRMS→Datenbank
    Gibt es eine Systemvorlage (Scope SYSTEM)? Gesucht wird in der System-Datenbank
  4. 4
    CRMS
    nimmt die erste Ausgabe, die in einer passenden Sprache eine veröffentlichte Revision hat
    Ergebnis: Gibt es keine: template-not-found. Eine Vorlage eines anderen Mandanten findet Ben nie.

Eingebettet liegen die Tabellen von CRMS neben denen der Anwendung, in denselben Datenbanken. Welche Sprache gilt und wie CRMS zurückfällt, steht unter Wie CRMS die Vorlage findet und Sprache und Zeitzone.

Etappe 5: Prüfen, rendern, antworten

render(FINAL, audit-report, audit-4711, …) in CRMS
  1. CRMS
    Format
    Bietet die Installation PDF an, und ist das Formatmodul da?
    ↳ nein format-not-offered|pdf
  2. CRMS
    Modus
    Speichert die Installation endgültige Dokumente?
    ↳ nein document-revisions-disabled
  3. CIAS
    Aufrufer
    Gibt es eine angemeldete Person?
    ↳ nein crms-authentication-required
  4. CRMS
    Vorlage
    Findet Ben eine veröffentlichte Revision von audit-report?
    ↳ nein template-not-found
  5. CRMS
    Ausgabetyp
    Passt PDF zum Ausgabetyp der Vorlage?
    ↳ nein format-not-for-template|pdf|<ausgabetyp>
  6. CRMS
    Payload
    Hält die Payload die Strukturregeln und die Felder der Vorlage ein?
    ↳ nein payload-rejected mit allen Verstößen
  7. CRMS
    Rendern
    Läuft die Vorlage im Pool durch, innerhalb von Zeit- und Größengrenze?
    ↳ nein render-timeout, render-output-too-large, render-capacity-exhausted …
  8. Das Dokument ist fertig. Bei FINAL wird es jetzt abgelegt

Bis zur Payload läuft alles auf dem Thread der Anfrage, denn nur dort gibt es den RequestContext und damit die richtige Datenbank. Das eigentliche Rendern läuft in einem begrenzten Pool, siehe Ausführung und Grenzen.

Bei FINAL kommt danach der Schritt, der den Bericht zum Nachweis macht:

Ein endgültiges Dokument ablegen
  1. 1
    CRMS
    siegelt das PDF, wenn die Installation siegelt
  2. 2
    CRMS
    erzeugt einen Schlüssel nur für dieses Dokument und verschlüsselt die Bytes damit
  3. 3
    CRMS→Dateispeicher
    legt die Bytes auf WORM-Speicher ab, gesperrt bis zum Ende der Aufbewahrung
    WORM heißt „write once, read many“: Ein Objekt lässt sich bis zu seinem Datum weder ändern noch löschen.
  4. 4
    CRMS→Datenbank
    hängt die Revision an die Kette von audit-report + audit-4711, mit Bens Kennung, Zeit, Hashes und retainUntil, und schreibt document-stored ins Journal
    Das Dokument gehört dem Mandanten der Anfrage und liegt in dessen Datenbank. Ohne Mandant liegt es in der System-Datenbank.
  5. 5
    CRMS→Anwendung
    gibt die Bytes zurück, zusammen mit der neuen Revision
    Ergebnis: Die Bytes kommen erst, wenn sie abgelegt sind. Ein endgültiges Dokument, das Ben hat und CRMS nicht, gibt es nicht.

Was eine Revision genau festhält, steht unter Was ein endgültiges Dokument ist, wie die Kette Änderungen aufdeckt unter Die zwei Hash-Ketten.

Etappe 6: Zurück zu Ben

Die Anwendung gibt das Dokument an den BFF, der BFF an den Browser. Geht etwas schief, kommt aus CRMS eine CrmsException mit einem Schlüssel, nie ein Satz. Die Anwendung reicht den Schlüssel unverändert weiter und übersetzt ihn nicht. Das Frontend zeigt ihn in Bens Sprache an. Der Hub macht es so: Er wandelt jede CrmsException in seine eigene Fehlerform um, mit demselben Schlüssel und dem Status, den CRMS für diesen Schlüssel nennt. Die Schlüssel stehen unter Die Fehlerantwort.

Eingebettet oder als eigener Dienst

Wo CRMS läuft, leitet der Hub aus der Laufzeit des CRMS-Moduls ab, siehe Eingebettet oder als eigener Dienst. Dein Code ruft in beiden Fällen denselben Port auf und bekommt dieselben Objekte und Schlüssel zurück. Unterwegs passiert aber Verschiedenes.

Derselbe Bericht, eingebettet und als eigener Dienst

Wann: Das CRMS-Modul liegt in der Laufzeit der Anwendung (codamai.crms.placement=local).

sequenceDiagram
    autonumber
    participant A as Anwendung
    participant FK as Filterkette (CIAS)
    participant R as CRMS (im Prozess)
    participant DB as Mandanten-DB
    A->>R: render(…) als Methodenaufruf
    R->>FK: wer ruft?
    FK-->>R: Ben, nordbau, Rollen (aus dem RequestContext)
    R->>DB: Vorlage suchen, bei FINAL Kette schreiben
    R-->>A: RenderedDocument

Ergebnis: Kein Netzwerk, kein zweites Token. Vorlagen und Dokumente liegen in den Datenbanken der Anwendung, ein FINAL wird in der Transaktion der Anfrage geschrieben.

Wann: Das CRMS-Modul liegt in einer anderen Laufzeit (codamai.crms.placement=remote).

sequenceDiagram
    autonumber
    participant A as Anwendung
    participant CL as crms-client
    participant FK as Filterkette im CRMS-Dienst
    participant K as Keycloak
    participant R as CRMS-Dienst
    participant DB as DB des CRMS-Dienstes
    A->>CL: render(…)
    CL->>CL: Bens Token aus der laufenden Anfrage
    CL->>FK: POST /crms/render, Authorization: Bearer (Bens Token)
    FK->>K: Token tauschen für den Client des Dienstes
    K-->>FK: Token mit den CRMS-Rollen
    FK->>FK: Mandant aus dem Token, Tor, Rollen
    FK->>R: RequestContext gefüllt
    R->>DB: Vorlage suchen, bei FINAL Kette schreiben
    R-->>CL: Bytes und Crms-*-Header
    CL-->>A: dasselbe RenderedDocument

Ergebnis: Die Person reist mit: Der Aufruf trägt Bens Token, nie ein Dienst-Token. Der Dienst hat eine eigene Filterkette, ein eigenes Mandanten-Tor und eine eigene Datenbank.

Drei Folgen des eigenen Dienstes:

  • Die Rollen kommen vom Client des Dienstes. Der Dienst tauscht Bens Token gegen eines für seinen eigenen Client. Eine Dokumentrolle muss Ben also an diesem Client haben, nicht am Client der Anwendung. Siehe Token-Tausch.
  • Der Mandant kommt nur aus dem Token. crms-client reicht allein die Kopfzeile Authorization weiter, keine Kopfzeilen tenant oder user. Der Dienst bestimmt den Mandanten so, als käme die Anfrage ohne diese Kopfzeilen.
  • Ohne laufende Anfrage gibt es kein Token. Ein zeitgesteuerter Job in der Anwendung bekommt crms-authentication-required, und nichts wird gesendet.

Wie der Client Antworten in Exceptions übersetzt, steht unter Der Client für den eigenen Dienst.

Vorschau, endgültig, flüchtig

Der Weg bis zum Rendern ist für alle drei Modi derselbe. Was danach passiert, unterscheidet sich:

Was der Modus am Ende des Weges ändert
PREVIEWFINALTRANSIENT
WofürBen sieht sich den Bericht an, bevor er ihn abschließtder Nachweis, etwa der abgeschlossene Prüfberichteine Arbeitskopie zum Weitergeben, etwa die Projektdokumente des Hubs
Referenzneinja, Pflichtnein
Vorlage ausjedem Scope, den Ben siehtjedem Scope, den Ben siehtnur dem Scope SYSTEM
Gespeichertneinja: verschlüsselt auf WORM-Speicher, Revision in der Kette mit Bens Kennungnein
Im Dokumentsichtbares Wasserzeichen PREVIEW, Markierung crms-previewnichts Sichtbares, PDF mit Siegel, wenn eingerichtetkein Wasserzeichen, Markierung crms-transient in den Metadaten
Eingeschaltetimmersolange die Installation Dokumentrevisionen speichertnur wenn die Installation es einschaltet

Ein typischer Ablauf nutzt zwei Modi: Ben klickt erst auf „Vorschau“ (PREVIEW), prüft das Ergebnis und schließt dann ab (FINAL). Beide Aufrufe gehen genau denselben Weg und rendern mit derselben Vorlage, nur der zweite legt etwas ab. Alle Regeln der Modi stehen unter Vorschau, endgültig, flüchtig.

Was in welchem Aufruf steckt

AbschnittWas geht mitWer prüft es
Browser → BFFSitzungs-Cookieder BFF
BFF → AnwendungBens Access-Token, dazu die ID des Auditsdie Filterkette von CIAS
Anwendung → CDMSnichts Neues: derselbe RequestContextCDMS mit Rollen und Filtern
Anwendung → CRMS, eingebettetRenderRequest als Java-ObjektCRMS, mit dem Aufrufer aus CIAS
Anwendung → CRMS-DienstPOST /crms/render mit Bens Token und der Anfrage als JSONdie Filterkette im CRMS-Dienst, dann CRMS
CRMS → Datenbank und Dateispeicherkeine Anmeldedaten der Person, sondern die Verbindungen der InstallationDatenbank und Speicher selbst

Wo der Weg endet, wenn etwas fehlt

Warum der Bericht nicht kommt
Filterkette lässt durchVorlage gefundenPayload gültigRendern gelingtWas Ben bekommt
nein–––401 oder 403 aus der Filterkette, mit cias.authentication.…. CRMS wurde nie gefragt
janein––template-not-found, als 404
jajanein–payload-rejected mit violations, als 422. Die Anwendung hat falsche Daten gebaut
jajajaneinein render-…-Schlüssel, etwa render-timeout als 504
jajajajadas Dokument, bei FINAL zusätzlich abgelegt

Fallen

Wie es weitergeht

Suchen