CodamAIDocs
Themafertig

Generierter Code und eigener Code

Warum generierter Code nie von Hand geändert wird und wo eigener Code hingehört: Hooks, eigene Filter, eigene Controller und Services, Konfiguration.

Ausprägungen
generiert, nicht anfassenHookeigener FilterAttributfiltereigener Controller oder ServiceKonfiguration

Worum es geht

In einem CDMS-Projekt gibt es zwei Sorten Code. Den generierten Code erzeugt der Build aus dem Modell im Hub, siehe Codegenerierung im Build. Den eigenen Code schreibst du: Fachlogik, die sich nicht modellieren lässt.

Die beiden liegen an verschiedenen Orten und haben verschiedene Besitzer. Wer das trennt, kann jederzeit neu generieren, ohne etwas zu verlieren.

Wer wo wohnt

flowchart TB
    subgraph T["target/generated-sources/ · generiert, bei jedem Build neu"]
        A["OrderApi"] --> S["OrderSystem"] --> D["OrderDatabase"]
        S --> AL["OrderAuthorizationLayer"]
        M["OrderEntity, OrderDto, Payloads, Mapper, MetaService"]
    end
    subgraph SRC["src/main/java/ · deins, im Repository"]
        H["OrderHook<br/>(Hook)"]
        F["OrderFilter<br/>(Filter)"]
        C["eigener Controller<br/>oder Service"]
        ST["Start.java<br/>(einmal erzeugt)"]
    end
    subgraph R["src/main/resources/ · deins"]
        Y["application.yaml"]
    end
    H -. "wird aufgerufen von" .-> S
    F -. "wird gefragt von" .-> S
    C -- "ruft auf" --> S
OrtBesitzerins Repository?was passiert beim Build
target/generated-sources/annotations/Generatorneinwird neu erzeugt
src/main/java/dujawird kompiliert, nie überschrieben
src/main/resources/dujawird mitgepackt, nie überschrieben
pom.xml zwischen den cdms-managed-MarkernHubjawird nachgezogen, siehe Projektrahmen

Wohin mit meiner Logik?

Welcher Erweiterungspunkt passt?
Was willst du tun?Erweiterungspunkt
ein Feld, eine Regel, einen Endpunkt, eine Rolle ergänzenModell im Hub ändern und neu bauen, kein Code
vor oder nach dem Speichern etwas prüfen, berechnen, auslösenHook auf das Modell
Zeilen je nach Benutzerattribut ausblendenZugriffsfilter im Hub, bei Bedarf eigene Filterklasse
jede Suche auf ein Modell nach eigener Logik einschränkeneigener Filter
einen fachlichen Endpunkt anbieten, den CDMS nicht kennteigener Controller, der den System-Layer aufruft
Verhalten von CDMS einstellenKonfiguration in application.yaml oder Umgebung
eine generierte Klasse änderngeht nicht, beim nächsten Build ist die Änderung weg

Hooks

Ein Hook ist eine Klasse, die CDMS bei jeder Operation auf ein Modell aufruft. Du schreibst sie für die Entity des Modells:

@Service
@Order(100)
public class OrderHook implements HookServiceInterface<OrderEntity> {

  @Override
  public void beforeDatabaseChange(OrderEntity item, CmsMethods method)
      throws AbstractCodamaiException {
    if (method == CmsMethods.CREATE && item.getOrderNr() == null) {
      item.setOrderNr(nextNumber());
    }
  }

  @Override
  public void afterDatabaseChange(OrderEntity item, CmsMethods method)
      throws AbstractCodamaiException {
    // e.g. publish an event
  }

  @Override
  public void fieldBeforeDatabaseChange(OrderEntity item, String field, CmsMethods method) { }

  @Override
  public void fieldOnDatabaseRead(OrderEntity item, String field, CmsMethods method) { }
}
  1. 1
    Entwickler
    schreibt eine Klasse, die HookServiceInterface<OrderEntity> implementiert
  2. 2
    Entwickler
    macht sie mit @Service zur Spring-Bean und gibt ihr ein @Order
    @Order legt die Reihenfolge fest, wenn es mehrere Hooks auf dasselbe Modell gibt. Kleinere Zahl zuerst. Setze es direkt an die Klasse.
  3. 3
    CDMS
    sucht alle Hook-Beans für OrderEntity und sortiert sie nach @Order
  4. 4
    Hook
    wird bei jeder Operation aufgerufen, mit der Operation als CmsMethods
    CREATE, READ, UPDATE, PATCH, DELETE, ROLLBACK

Die Arbeit machen beforeDatabaseChange (vor dem Schreiben, in der Transaktion) und afterDatabaseChange (nach dem Schreiben, beim Lesen nach dem Laden). Wirft ein Hook eine AbstractCodamaiException, bricht die ganze Operation ab und die Transaktion wird zurückgerollt. Wann genau welcher Hook läuft, steht unter Hooks: Arten und Zeitpunkte und Reihenfolge der Hooks.

Eigene Filter

Ein Filter schränkt jede Suche auf ein Modell ein, bevor sie die Datenbank erreicht. Er arbeitet mit dem DTO des Modells. Der häufigste Fall ist ein Attributfilter: Jeder Benutzer sieht nur die Zeilen, deren Feld zu einem Attribut in seinem Token passt. Dafür gibt es die fertige Basisklasse AbstractAttributeFilter:

@Service
public class OrderFilter extends AbstractAttributeFilter<OrderDto> {
  @Getter
  private final String dataAttribute = "companyId";   // field on the model
  @Getter
  private final String profileAttribute = "company";  // attribute in the user's token
}
Was der Attributfilter aus dem Token macht
Wert des Attributs im TokenBedingung in der Suche
ein Wert, z. B. c-17companyId = 'c-17'
mehrere WertecompanyId IN (…)
*kein Filter, alle Zeilen
fehlt oder leerAnfrage wird abgelehnt

Brauchst du eine andere Logik, implementierst du CdmsFilterInterface<OrderDto> direkt und lieferst in get() einen eigenen Suchfilter. Legst du im Hub einen Zugriffsfilter an, erzeugt der Generator die Filterklasse selbst. Mehr unter Attributfilter und Eigene Datenfilter.

Eigene Controller und Services

Für fachliche Abläufe, die über Anlegen, Lesen, Ändern und Löschen hinausgehen, schreibst du einen normalen Spring-@RestController. Er ruft die generierten System-Layer auf. So laufen auch seine Zugriffe durch Rechteprüfung, Validierung und Hooks.

Den generierten OrderApi erweiterst oder ersetzt du dagegen nicht. Die generierten Beans haben feste Namen. Eine zweite Bean mit demselben Namen verhindert den Start der Anwendung.

Konfiguration

Was sich an CDMS einstellen lässt, steht in application.yaml oder kommt aus der Umgebung:

Propertywofür
codamai.cdms.api.createReadModewie streng das Zurücklesen nach dem Anlegen ist (STRICT, LENIENT)
codamai.cdms.cias.reader-rolesRollen, die die Rollendeklaration lesen dürfen
codamai.cdms.persistence.file.basePathAblageort für Dateien
codamai.debugStacktrace in Fehlerantworten (im Betrieb false)
CODAMAI_PERSISTENCE_TENANT_MODESINGLE oder MULTI
CODAMAI_PERSISTENCE_DATABASE_*Treiber, URL, Benutzer, Passwort der Datenbank

Fallen

Quellen im Code und in der Wissensdatenbank
  • CDMS/cdms-generator – generator/CdmsProcessor, AbstractProcessor (createSourceFileOrSkip), StartClassProcessor
  • commons – global/commons/interfaces/HookServiceInterface, CmsMethods
  • CDMS/cdms-system-layer – HookManagementSystem, AbstractLayer (getCdmsFilter)
  • CDMS/cdms-commons – interfaces/CdmsFilterInterface
  • CDMS/cdms-authorization – security/AbstractAttributeFilter
  • CDMS/cdms-integrationtest – order/OrderFilter, vault/VaultFilter
  • hub-backend – services/FolderHook, ModelDesignApi
  • documentation/60-erweiterung/01-hooks.md, 03-eigene-filter.md
Suchen