Hauptteil

Zugangskonzept

Allgemeines

Die Zugriffe auf die WebAPI erfolgen auf Basis eines Benutzerkontos (NetDB-Konto) und eines zugeordneten Access Tokens. Um das Risiko der Kompromittierung eines Kontos zu minimieren und mögliche Einsatzprofile besser strukturieren zu können, gibt es in der NetDB 2 Arten von Benutzerkonten:

  • Hauptkonten, mit den beiden Varianten
    • KIT-Benutzerkonten, primär im KIT-IDM verwaltet. Dh. das Benutzerkonto muss im IDM existieren, bevor es in der NetDB angelegt werden kann.
    • NetDB-Dienstkonten, ausschließlich in NetDB verwaltet (nicht im IDM vorhanden).
  • Unterkonten für beide Varianten der Hauptkonten, ausschließlich in NetDB verwaltet (nicht im IDM vorhanden).

Über ein Konto werden die Zugriffsrechte der ihm zugeordneten Access Token abgebildet. Die Tokenvergabe ist wie folgt geregelt:

  • KIT-Benutzerkonten sind zur direkten Nutzung des Web-Portals (NETVS) vorgesehen und verwenden temporäre (Session-)Token. Diese Token werden automatisch bei Anmelden im NETVS bereitgestellt.

  • NetDB-Dienstkonten sind zur indirekten Nutzung des Web-Portals (NETVS) vorgesehen. Indirekt bedeutet, dass sie via Sudo-Feature als ausführendes Konto einer Transaktion eingesetzt werden (s. Verwendung durch zugeordnete KIT-Benutzerkonten). Sie verwenden daher kein NETVS-Session-Token, können aber ebenso Unterkonten haben, die den direkten WebAPI-Zugang über statische Token nutzen. Die Administration erfolgt ausschließlich durch OE-Betreuer.

  • Unterkonten sind ausschließlich für den direkten WebAPI-Zugang vorgesehen und verwenden statische Token. Deren Token müssen vom Inhaber des Hauptkontos über NETVS eingerichtet werden. Ist das Hauptkonto ein Dienstkonto, erfolgt die Einrichtung durch die KIT-Benutzerkonten der Gruppe des Dienstkontos.

Unterkonten können vom Inhaber eines Hauptkontos jederzeit nach eigenem Ermessen angelegt, modifiziert oder gelöscht werden. Jedem Unterkonto (und den zugehörigen Access Token) ist dadurch genau ein Hauptkonto zu- und übergeordnet. Ebenso kann der Inhaber des Hauptkontos im Rahmen seiner eigenen Rechte auch die Rechte seiner Unterkonten (Rollen und Untergruppen mit Namensräumen bzw. Adressräumen) verwalten. Zusätzlich kann ein Konto als ‘Nur-Lese-Konto’ eingestellt werden. Dadurch ist der Zugriff auf datenmodifizierende WebAPI-Funktionen gesperrt. Diese Sperre ist rein funktional und hat daher Vorrang vor allen anderen Rechten und wirkt davon unabhängig.

Auch diese Einstellung kann der Inhaber des Hauptkontos jederzeit für alle seine Unterkonten ändern. Sie kann für KIT-Hauptkonten nur durch ein anderes Konto mit erweiterten Rechten zurückgenommen werden.

Unterkonten haben eine funktionale Sperre für

  • die Anmeldung im NETVS (da sie kein Passwort haben)
  • sicherheitsrelevante Funktionen (Modifizierungen an Konten einschl. Rechte- bzw. Rollenzuordnungen)

Diese Sperre wirkt ebenfalls unabhängig davon, ob das Unterkonto tatsächlich die benötigten Rechte hat. Die gesperrten Funktionen können daher nur mittels Hauptkonto ausgeführt werden. Unterkonten dürfen nur ihre eigenen Access-Token modifizieren.

Gruppen und Konten für Bereichsbetreuer

Gruppen dienen der effizienten Zuordnung der Netzbereiche (Adressräume, Namensräume) zu Benutzerkonten. Einer Gruppe sind BCDs, Domains und Benutzerkonten zugeordnet. Die zugeordneten Benutzerkonten werden auch als Gruppenmitglieder bezeichnet. Den Gruppenmitgliedern sind somit Domains und BCDs zugeordnet. Analog zu den beiden Kontenarten gibt es auch 2 Arten der Gruppen:

  • Hauptgruppen (können nur Hauptkonten enthalten; können als KIT-Benutzergruppen primär im IDM oder primär in NetDB verwaltet werden)
  • Untergruppen (enthalten jeweils genau ein Hauptkonto des Eigentümers und können darüberhinaus nur dessen eigene Unterkonten enthalten; nur primär in NetDB verwaltet; nicht im IDM vorhanden)

Domain- und BCD- Zuordnungen in Untergruppen können nur im Rahmen vorhandener Zuordnungen ihrer Hauptgruppe bestehen.

  • Der Namensraum aller Gruppenmitglieder wird durch die Domains ihrer Gruppen definiert.
  • Der Adressraum aller Gruppenmitglieder wird durch die Subnetze der BCDs ihrer Gruppen definiert.

Hauptgruppen in der NetDB können in 2 Modi benutzt werden:

  • IDM-synchron: alle Gruppenmitglieder werden primär über die IDM-Gruppenverwaltung zugeordnet. Beim Synchronisationsvorgang wird der Stand der Gruppenmitglieder der IDM-Gruppe auf die NetDB-Gruppe übertragen. Wird der Name und/oder die Beschreibung der IDM-Gruppe (idm-seitig) geändert, wird diese Namensänderung beim nächsten Synchronisationsvorgang auch auf die NetDB-Gruppe übertragen.
  • nicht-IDM-synchron: alle Gruppenmitglieder werden primär in der NetDB verwaltet.

Wird ein KIT-Benutzerkonto einer Hauptgruppe im IDM zugeordnet, und möchte der Kontoinhaber eine eigene Untergruppe mit eigenen Unterkonten erstellen, kann er dies erst, nachdem die Hauptgruppe synchronisiert wurde (und die Gruppenmitglieder aktualisiert sind). Umgekehrt werden Untergruppen und deren Unterkonten automatisch aus einer Hauptgruppe entfernt, wenn auch ihr Hauptkonto aus der Hauptgruppe entfernt wurde (unabhängig davon, ob die Hauptgruppe IDM-synchron ist oder nicht). Die automatische Synchronisation erfolgt dabei nicht in Echtzeit, sondern stündlich für alle Gruppen. Sie kann aber auch jederzeit explizit für eine bestimmte Gruppe angefordert werden, um eine sofortige Synchronisation zu erreichen. Der Synchronisationsvorgang ist IDM-seitig nur-lesend; alle Schreibzugriffe erfolgen ausschließlich NetDB-seitig.

Administration der Gruppen durch OE-Betreuer

Für die Gruppenadministration ist die Zuordnung eines Benutzerkontos zu einer Organisationseinheit (OE) erforderlich. Diese Zuordnung wird nicht vom IDM übernommen, sondern primär in der NetDB abgebildet. Der Inhaber des zugeordneten Kontos wird dadurch OE-Betreuer dieses OE-Teilbaums (dh. der zugeordneten OE einschließlich aller darunterliegenden OE) und bekommt das Administrationsrecht für

  • alle Hauptgruppen, die einer OE seines OE-Teilbaums zugeordnet sind und
  • alle Dienstkonten, die einer OE seines OE-Teilbaums zugeordnet sind. Dienstkonten haben dadurch keine OE-Administrationsrechte.

Ebenso bekommt er das Administrationsrecht für alle Domains und BCDs, deren OEs seinem OE-Teilbaum zugeordnet sind. Zusammengefasst:

  • Alle Netzbereiche (BCDs), die seinem OE-Teilbaum zugeordnet sind, bilden den Adressraum des OE-Betreuers.
  • Alle Domains, die seinem OE-Teilbaum zugeordnet sind, bilden den Namensraum des OE-Betreuers.
  • Die Mitgliedschaft des OE-Betreuerkontos in den Hauptgruppen des OE-Teilbaums ist davon unabhängig.

Das Administrationsrecht für Hauptgruppen, deren OE im eigenen OE-Teilbaum liegt, umfasst außerdem:

  • Eintragen neuer Gruppen
  • Ändern der Gruppenattribute (Name, OE, Beschreibung, IDM-Synchronisation)
  • Eintragen oder Löschen der Mitglieder
  • Eintragen der Domains (oder deren Subdomains), soweit sie dem eigenen OE-Teilbaum zugeordnet sind und
    • bei rekursiver Wirkung die Domain-OE gleich oder oberhalb der Gruppen-OE liegt
    • bei nicht-rekursiver Wirkung die Domain-OE gleich der Gruppen-OE ist
  • Eintragen der BCDs, soweit sie dem eigenen OE-Teilbaum zugeordnet sind und
    • bei rekursiver Wirkung die BCD-OE gleich oder oberhalb der Gruppen-OE liegt
    • bei nicht-rekursiver Wirkung die BCD-OE gleich der Gruppen-OE ist
  • Löschen der BCDs, Domains

Untergruppen werden ausschließlich durch ihre Eigentümer administriert.

Administration der Dienstkonten durch OE-Betreuer

Die besondere Eigenschaft in der Administration der Dienstkonten besteht darin, dass diese nicht an ein einzelnes KIT-Benutzerkonto gebunden ist, sondern immer durch alle derzeitigen OE-Betreuer vorgenommen werden kann.

Scheidet ein OE-Betreuer als Person aus oder kommt eine neue Person als OE-Betreuer hinzu, überträgt sich die Administrationsfähigkeit automatisch (durch die gleiche OE-Zuordnung). Das Administrationsrecht der OE-Betreuer für Dienstkonten umfasst:

  • Eintragen neuer Dienstkonten und diesen mindestens eine ihrer eigenen OEs zuordnen (transaktional gleichzeitig)
  • Ändern der Kontenattribute (Dienst-Kennung, Beschreibung, Nur-Lesezugriff)
  • Ändern der OE-Zuordnung (neue OE zuordnen; bestehende OE-Zuordnung löschen)
  • Ändern der Hauptgruppen-Zuordnung (Eintragen in bzw. Löschen aus Hauptgruppen, deren OE im eigenen OE-Teilbaum liegt)
  • Löschen des Dienstkontos,

wenn alle OEs des Dienstkontos auch in allen OE-Teilbäumen des OE-Betreuerkontos liegen. Diese Bedingung wird bei Neueintrag durch die transaktional gleichzeitige OE-Zuordnung erfüllt. Dienstkonten haben keine OE-Administrationsrechte, wie es bei Hauptkonten der Fall ist.

Gruppen, die IDM-synchron gehalten werden, können kein Dienstkonto enthalten, da NetDB-Dienstkonten IDM-seitig nicht bekannt sind und daher bei der Synchronisation gelöscht werden würden.

Verwendung der Dienstkonten durch KIT-Benutzerkonten

Die besondere Eigenschaft in der Nutzung der Dienstkonten besteht darin, dass diese nicht auf ein einzelnes KIT-Benutzerkonto beschränkt ist, sondern für mehrere KIT-Benutzerkonten durch eine Gruppenmitgliedschaft festgelegt werden kann.

Dienstkonten können durch KIT-Benutzerkonten, die sich in derselben Hauptgruppe wie das Dienstkonto befinden, als ausführendes Konto einer Transaktion (Schreib-Lese-Zugriff) eingesetzt werden (via Sudo-Feature, s. Transaktionsparameter su_login_name im Zielabschnitt).

OE-Betreuer müssen sich selbst dieser Gruppe zuordnen, um dieses Recht auszuüben. Damit sind KIT-Hauptkonten einer Gruppe berechtigt, Transaktionen mit den Rechten eines dieser Gruppe zugeordneten Dienstkontos auszuführen. Diese benötigte Gruppenzuordnung obliegt der Administration durch OE-Betreuer.

Administration der Dienst-Unterkonten und Dienst-Untergruppen durch KIT-Benutzerkonten

Dienst-Unterkonten und Dienst-Untergruppen werden (in analoger Weise zu eigenen Unterkonten/Untergruppen) administriert, indem das Dienst-Hauptkonto als eingesetztes Konto durch KIT-Benutzerkonten (wie oben beschrieben) verwendet wird.

Authentifizierung

WebAPI-Zugriffe (Requests) werden über ein token-basiertes Verfahren (OAuth 2.0 Bearer Token) authentifiziert. Dazu kann jedem Unterkonto ein oder mehrere sog. Access Token zugeordnet werden. Über diese Zuordnung erfolgt die Identifikation. Der WebAPI-Zugriff erfolgt also mit den Rechten des Kontos, das dem verwendeten Access Token zugeordnet ist. Das Token bzw. dessen Authentifizierungstext wird vom System (NetDB) automatisch generiert und ist sicherheitstechnisch wie ein geheimer Schlüssel bzw. Klartext-Passwort zu betrachten. Bitte beachten Sie auch die Sicherheitshinweise dazu.

Die Registrierung eines Tokens muss je nach Umgebung über die NETVS-Webseite

vorgenommen werden.

Ein Access Token umfasst folgende Hauptdaten:

  • Benutzerkennung des Kontos
  • Ablaufdatum (kann auch leer sein, falls Token unbegrenzt gültig sein soll). Token, deren Ablaufdatum überschritten sind, sind für den WebAPI-Zugriff gesperrt. Sie bleiben aber bestehen, bis das Löschdatum erreicht ist.
  • Löschdatum: Datum der endgültigen Löschung des Tokens. Wird bei jedem Zugriff automatisch verlängert (je nach Typ, s. dort)
  • zuletzt angemeldet: Datum des letzten Zugriffs, der mit diesem Token erfolgte
  • zuletzt generiert: Datum, wann der aktuell gültige Authentifizierungstext erzeugt wurde
  • Beschreibungstext: benutzerseitige Information, frei wählbar
  • Authentifizierungstext: das eigentliche Identifizierungsmerkmal des Benutzerkontos (systemgeneriert). Wird nur einmalig nach Erzeugen oder Ändern angezeigt, danach nicht mehr abfragbar.
  • Typ, Art des Tokens:
    • statisch, Inaktivitätsintervall 200 Tage, nur für Unterkonten
    • temporär mit kurzem Inaktivitätsintervall (1 Stunde), nur für Hauptkonten
    • temporär mit langem Inaktivitätsintervall (1 Monat), nur für Hauptkonten
  • gpk: Globaler Primärschlüssel

Statische Token werden für den WebAPI-Zugriff der Unterkonten verwendet. Der Inhaber des Hauptkontos kann für statische Token seiner Unterkonten jederzeit einen neuen Authentifizierungstext vom System generieren lassen, ebenso kann er das Ablaufdatum und den Beschreibungstext jederzeit neu bestimmen oder das Token löschen. Wichtig: Löschbare Token werden täglich automatisch gelöscht. Das Löschdatum ist erreicht, wenn

  • das Ablaufdatum um 200 Tage überschritten ist
  • kein Ablaufdatum definiert ist und die letzte Benutzung (Login) älter als 200 Tage ist
  • das Token unbenutzt, kein Ablaufdatum definiert und das Erzeugungsdatum älter als 200 Tage ist

Eine Löschung (bspsw. selten genutzter Token) kann verhindert werden, indem rechtzeitig eine Re-Validierung durch z.B. einen Request mit leerer Transaktion ausgeführt wird.

Temporäre Token werden typischerweise für Web-Browser-Sessions der Hauptkonten verwendet. Sie werden nach Überschreiten des Ablaufdatums, maximal aber nach 1 Monat automatisch gelöscht. Bei Token mit kurzem Inaktivitätsintervall wird das Ablaufdatum mit jedem Zugriff um das festgelegte Intervall (1 Stunde) verlängert. Der Inhaber des Hauptkontos kann eigene temporäre Token jederzeit löschen; Änderungen an temporären Token sind gesperrt.

Hinweise zur Registrierung von Unterkonten

Wir empfehlen Ihnen aus Sicherheitsgründen (Schadensminimierung im Mißbrauchsfall):

  • für den WebAPI-Zugang nicht Ihr persönliches, ggf. sogar höher privilegiertes Benutzerkonto zu verwenden, sondern je nach Einsatzprofil ein oder mehrere Unterkonten mit den mindestnötigen Privilegien anzulegen und einzusetzen.
  • die Trennung der Einsatzprofile nach Nur-Lese-Zugriffen und Schreib-Lese-Zugriffen, um im ersten Fall Nur-Lese-Konten einzusetzen.

Sicherheitshinweise zur Token-Benutzung

Der Authentifizierungstext ist das Identifizierungsmerkmal des eindeutig zugeordneten Benutzerkontos und ist deshalb wie ein privater bzw. geheimer Schlüssel (Klartext-Passwort) zu betrachten. Der Besitzer des Authentifizierungstextes kann WebAPI-Requests mit den Rechten des zugeordneten Kontos ausführen. Um einen Mißbrauch bzw. Kompromittierung zu vermeiden, beachten Sie bitte folgendes:

  • den Authentifizierungstext nur in einer Umgebung speichern, in der der Zugriff unberechtigter Dritter nach aktuellen Sicherheitsstandards ausgeschlossen ist.
  • für zeitlich begrenzte Einsatzprofile das Ablaufdatum im Voraus entsprechend einstellen (ein z.B. ‘vergessenes’ Token ist dadurch automatisch deaktiviert). Nach Überschreiten des Ablaufdatums kann das Token zwar nicht mehr benutzt werden, bleibt aber für weitere 200 Tage in der NetDB gespeichert. In dieser Zeit kann es durch Ändern der Ablaufdatums wieder freigeschaltet werden, ohne dass der Authentifizierungstext verändert werden muss.

URI-Schema

Alle Zugriffe (Requests) werden über URIs adressiert, deren Aufbau nach folgendem Schema erklärt wird:

  • Werte in spitzen Klammern sind Platzhalter
  • Werte in eckigen Klammern sind optional
  • großgeschriebene Platzhalter sind konstant; kleingeschriebene sind variabel
  • Beschreibungstext wird von Anführungszeichen "" umgeben
<BASE-URI>/<vmajor>.<vminor>/<sys>/<objtype>/<function>[<params>]

<BASE-URI>: https://api.netdb.scc.kit.edu       "Produktionsumgebung"
            https://api.netdb-test.scc.kit.edu  "Testumgebung (täglich veraltete Kopie der Produktionsumgebung)"
            https://api.netdb-devel.scc.kit.edu "Entwicklungsumgebung (unbestimmt veraltete Kopie der Produktionsumgebung mit Modifikationen)"

<vmajor>  : "natürliche Zahl, Hauptnummer der Version (Versionsreihe, Major Release)"
<vminor>  : "natürliche Zahl, Unternummer der Version innerhalb der Versionsreihe (Minor Release)"

<sys>     : "WebAPI-Systemname, Teilbereich von Anwendungsdaten (z.B. dns, nd)"
<objtype> : "WebAPI-Objekttyp-Bezeichnung (z.B. record, module, fqdn)"
<function>: "WebAPI-Funktion: Zugriffsart (z.B. create, update, delete, list)"

<params>  : "Parameterzeichenkette (query string) der Form ?<param1>=<value1>[&<param2>=<value2>[&...]]"

Alle rechtsseitig bzw. unterhalb von <BASE-URI> stehenden Platzhalter im URI-Schema sind demnach variabel. Ihre Wertebelegungen sind bei Versionswechseln als potentiell veränderbar zu betrachten und können daher versionsweise unterschiedlich sein. Eine Ausgabe der Wertebelegungen der variablen Platzhalter kann über Indexabfragen oder über die Ausgabe (list-Funktion) der Objekttypen /wapi/system, /wapi/object_type, /wapi/function erreicht werden. Der jeweilige URI zur Dokumentation kann durch Abfrage des <BASE-URI> erreicht werden; er ist dort unter dem Bezeichner "doc_uri" hinterlegt.

Die Inhalte der drei angegebenen Datenbanken (Produktions-, Test- und Entwicklungsumgebung) sind für sich unabhängig voneinander, aber:

  • die Testdatenbank wird täglich nachts von der Produktionsdatenbank kopiert. Dort gemachte Änderungen sind also maximal für einen Tag sichtbar, weil sie anschließend wieder vom Stand der Produktionsdatenbank überschrieben werden.
  • Die Entwicklungsdatenbank wird in unregelmäßigen Abständen von der Produktionsdatenbank kopiert; zusätzlich sind dort Modifikationen im Rahmen der Weiterentwicklung möglich. Selbstverständlich gibt es hierbei keinerlei Garantien oder Support.

Deren Datenbestände sind also “Momentaufnahmen” der Produktionsdatenbank und grundsätzlich als veraltet anzusehen. Demnach müssen auch ggf. neue Konto- bzw. Token-Registrierungen, die sofort auch in Test- und Entwicklungsumgebung benötigt werden, dort separat vorgenommen werden.

Requests

Ein Request entspricht einer Datenbank-Transaktion (TA).

Der Status einer TA kann aus der Antwort über den HTTP-Header netdb-transaction-state abgefragt werden. Definierte Stati:

  • not_executed: die Transaktion wurde nicht ausgeführt (aufgrund eines vorausgehenden Fehlers, z.b. Authentifizierung)
  • committed: die Transaktion wurde fehlerfrei ausgeführt
  • rolled_back: die Transaktion wurde fehlerfrei ausgeführt, aber zurückgerollt (implizites oder explizites Transaktionsrollback)
  • failed: die Transaktion wurde zwar ausgeführt, aber durch eine Exception abgebrochen und zurückgerollt

Der Datenzugriffsmodus der TA kann aus der Antwort über den HTTP-Header netdb-transaction-access-mode abgefragt werden. Definierte Modi:

  • not_available: nicht definiert (z.b. bei Exceptions oder leeren Transaktionen)
  • read_only: Nur-Lesezugriff, dh. die TA enthält keine datenmodifizierenden Funktionen
  • write_only: Nur-Schreibzugriff, dh. die TA enthält nur datenmodifizierende Funktionen, die keine Antwortdaten zurückgeben
  • read_write: Lese- und Schreibzugriff

Authentifizierung

Die Requests unterliegen grundsätzlich dem Zwang zur Authentifizierung. Davon ausgenommen sind Zugriffe, die ausschließlich auf den Systemdatenbereich erfolgen. Die Authentifizierung an sich ist nicht Bestandteil der eigentlichen Transaktion und wird vorher ausgeführt. Bei erfolgreicher Authentifizierung wird Zugriffsdatum und Zugriffszeit des Access Token aktualisiert, unabhängig davon, welches Ergebnis durch die Transaktion entsteht.

Authentifizierte Requests

Im HTTP-Header authorization muss ein gültiges OAuth 2.0 Bearer Token übergeben werden. Muster des HTTP-Headers:

authorization: Bearer ***AUTH-TOKEN-TEXT***

Bearer bezeichnet das Authentifizierungsschema; ***AUTH-TOKEN-TEXT*** ist der Authentifizierungstext des Access-Tokens. Zwischen beiden Angaben steht mindestens 1 Leerzeichen. Jedes Access-Token ist genau einem Benutzerkonto zugeordnet, dessen Rechte bei der Ausführung des Requests gelten.

Nicht-authentifizierte Requests

Der HTTP-Header authorization wird nicht angegeben oder ist leer.

Unterstützte Methoden

Als HTTP-Request-Methoden können POST (generell) und GET (nur für nicht-datenmodifizierende Funktionen) verwendet werden. Als Zeichenkodierung wird ausschließlich UTF-8 verwendet. Zur Ablehnung des Requests führen:

  • Authentifizierungsfehler
  • nicht als UTF-8 dekodierbare Werte
  • Übergabe von Parametern, die die jeweilige Funktion nicht erwartet
  • Parameterwerte, deren JSON-Datenyp nicht mit dem JSON-Datentyp des Parameters übereinstimmt
  • Parameterwerte, die sich nicht in den zugehörigen Datentyp konvertieren lassen

POST-Request

Diese Methode bietet die volle Funktionalität und ist für alle Funktionsaufrufe verwendbar. Der Request Body muss ein gültiges JSON-Dokument sein, dem JSON-Schema im Versionsindex unter dem Schlüssel transaction_json_schema entsprechen und auch als solches deklariert sein (HTTP-Header: content-type:application/json). Ein ungültiges JSON-Dokument führt zur Ablehnung des Requests.

Requests als Einzelbefehls-Aufruf

Bei dieser Variante wird pro Request genau ein Objekt eines Objekttyps eines Systems mit einer Funktion behandelt. Sollen mehrere Objekte desselben Objekttyps und Systems mit derselben Funktion nach dieser Variante behandelt werden, ist pro Objekt ein separater Request (separate Transaktionen) erforderlich; es sei denn, die Funktion gestattet die Behandlung mehrerer Objekte gleichzeitig (Bulk-Funktion). URI-Schema unterhalb der Version:

/<sys>/<objtype>/<function>

Datenstruktur des Request Body:

Da System, Objekttyp und Funktion bereits im URI adressiert sind, besteht das JSON-Dokument nur aus einem JSON-Dict mit den Schlüsseln old, new mit JSON-Dicts für die jeweiligen Parameter-Wertebelegungen. old beinhaltet die jeweiligen Parameterwerte vor der Änderung; new diejenigen nach der Änderung:

{
  "old": {
    "<param_name>": <param_value>, ...
  },
  "new": {
    "<param_name>": <param_value>, ...
  }
}

Requests als Mehrfachbefehls-Aufruf

Pro Request können hier mehrere Befehle (Statements) mit jeweils unterschiedlichen Systemen, Objekttypen und Funktionen behandelt werden. URI-Schema unterhalb der Version:

/wapi/transaction/execute

Im Gegensatz zum Einzelbefehls-Aufruf sind System, Objekttyp und Funktion hier nicht im URI festgelegt, sondern werden im Request Body pro Statement angegeben. Bei diesem generischen Transaktionsaufruf können zusätzliche Steuerparameter in der Parameterzeichenkette übergeben werden:

  • dry_mode (Boolean-Typ), Wertebelegung:

    • true gibt an, dass dieser Request (=Transaktion) nur als ‘Trockenlauf’ behandelt wird, d.h. alle Änderungen, die innerhalb des Requests stattfinden, werden zwar durchlaufen, aber an dessen Ende wieder verworfen (es erfolgt ein Datenbank-Transaktions-Rollback). Dieser Modus ist geeignet, um die Transaktion auf Fehler zu testen und daher nur sinnvoll, wenn datenmodifizierende Funktionen enthalten sind.
    • false (Standardwert) bewirkt die vollständige Abarbeitung des Requests, d.h. bei Fehlerfreiheit werden alle Änderungen gespeichert und sind für nachfolgende Requests sichtbar (es erfolgt ein Datenbank-Transaktions-Commit).
    • s.a. Abfrage des Transaktionsstatus
  • dict_mode (Boolean-Typ, s.a. Antwort-Datenstruktur), Wertebelegung:

    • true gibt an, dass die äußere Ebene des Antwort-JSON-Dokuments (Transaktionsebene) ein JSON-Dict ist. Dessen Schlüssel sind gleichlautend zum zugehörigen Statement-Index idx des Request Body.
    • false (Standardwert) gibt an, dass die äußere Ebene des Antwort-JSON-Dokuments (Transaktionsebene) ein JSON-Array ist. Dessen Positionen sind gleichlautend zur zugehörigen Statement-Position des Request Body.
  • su_login_name (Text-Typ)

    • Gibt den Login-Namen des einzusetzenden Benutzerkontos an, unter dessen Rechten die Transaktion ausgeführt werden soll.
    • Der Parameter darf bei nicht-eigenen Konten nur mit Administratorrechten eingesetzt werden, d.h. das authentifizierte Benutzerkonto ist Inhaber der Rolle wapi.sudoer. In diesem Fall wird die Transaktion zwingend zurückgerollt (unabhängig davon, welchen Wert dry_mode hat). Man kann damit feststellen, ob die Transaktion fehlerfrei gelaufen wäre bzw. welche Daten das eingesetzte Konto sehen würde.
    • Bei eigenen Dienst-Hauptkonten oder eigenen Unterkonten sind keine Administratorrechte erforderlich und die Transaktion wird nicht zwingend zurückgerollt.
    • s.a. Abfrage des Transaktionsstatus

Datenstruktur des Request Body:

Die gültige Struktur ist primär im JSON-Schema des Versionsindex unter dem Schlüssel transaction_json_schema hinterlegt. Da mehrere Statements möglich sind, besteht das JSON-Dokument aus einem äußeren JSON-Array, das die Transaktionsebene darstellt. Dieses enthält wiederum JSON-Dicts, welche jeweils ein Statement darstellen.

[
  {
    "title": "",
    "description": "",
    "idx": "<sidx>",
    "name": "<system_name>.<object_type_name>.<function_name>",
    "old": { "<param_name>": <param_value>, ... },
    "new": { "<param_name>": <param_value>, ... },
    "ref_params_join_on_val_attrs_tuple": ["<param_name", ...],
    "old_ref_params": [ <old_ref_params_dict>, ... ],
    "new_ref_params": [ <new_ref_params_dict>, ... ],
    "old_ref_idx": "<old_ref_sidx>",
    "new_ref_idx": "<new_ref_sidx>",
    "inner_join_ref": { "<ref_sidx>": "<referencing_constraint_name>" | "default" | "self" | null, ... },
    "anti_join_ref": { "<ref_sidx>": "<referencing_constraint_name>" | "default" | "self", ... },
    "semi_join_noref": { "and": ["<fk_constraint_name>", ...], "or": ["<fk_constraint_name>", ...] },
    "anti_join_noref": { "and": ["<fk_constraint_name>", ...], "or": ["<fk_constraint_name>", ...] },
    "when": { "<when_condition_function>": [ <when_condition_function_arg>, ... ] }
  }
]

Nachfolgend werden die Schlüssel der äußersten Ebene erklärt.

  • title: dient nur der Dokumentation bzw. Lesbarkeit des Statements
  • description: dient nur der Dokumentation bzw. Lesbarkeit des Statements
  • idx: Statement-Index, der eine eindeutige Bezeichnung des Statements innerhalb der Transaktion ermöglicht. Der Statement-Index kann abweichend von der Statement-Position willkürlich vorgegeben/benannt werden; d.h. er ist unabhängig von der Position. Er wird als Zielreferenz für JOIN-Anweisungen, WHEN-Anweisungen und RefParams-Anweisungen sowie als Statement-Index im Antwort-JSON-Dokument (nur mit dict_mode = true, s. Antwort-Datenstruktur) verwendet. Wird die Position eines Statements, das eine Zielreferenz darstellt, verschoben (z.B. durch veränderte Anwendungslogik), müssen die Ziele der JOIN/WHEN-Anweisungen bzw. Bezugsparameter folglich nicht verändert werden, da der Statement-Index unverändert bleiben kann. Grundvoraussetzung ist dabei immer, dass die Position des referenzierten Statements vor allen seinen referenzierenden Statements liegt. Wird kein Statement-Index angegeben, bekommt er automatisch die eigene Statement-Position. Diese ist immer 0-basiert, d.h. die Position des ersten JSON-Dicts (Statements) ist 0.
  • name: enthält die Angaben für System, Objekttyp und Funktion im Format <system_name>.<object_type_name>.<function_name>
  • old: statische Parameter-Altwerte; Schlüssel ist der Parametername, Wert ist der alte Parameterwert
  • new: statische Parameter-Neuwerte; Schlüssel ist der Parametername, Wert ist der neue Parameterwert
  • old_ref_params, new_ref_params: RefParams-Anweisung zur dynamischen Parameterübergabe als JSON-Array. Enthält Dicts mit Zielreferenz, Parameter-Mapping, Positions- und Offsetangaben der Ergebnisdatensätze und JOIN-Typ / JOIN-Bedingung
  • old_ref_idx, new_ref_idx: Kurzform der RefParams-Anweisung, wenn nur eine Zielreferenz ohne Parameter-Mapping benutzt wird
  • ref_params_join_on_val_attrs_tuple: Tupel unqualifizierter Attributnamen für wertebasierte JOINs in RefParams-Anweisungen
  • inner_join_ref: JOIN-Anweisung
  • anti_join_ref: JOIN-Anweisung
  • semi_join_noref: JOIN-Anweisung
  • anti_join_noref: JOIN-Anweisung
  • when: WHEN-Anweisung

Außer name sind alle Schlüssel je nach Kontext optional.

Die Schlüssel

werden nachfolgend detaillierter erklärt.

Ausgabe von Objekten relational verbundener Objekttypen mit JOIN-Anweisungen

Um relevante Ausgabedaten referenzierender oder referenzierter Objekte zu erhalten und die Datenmenge entsprechend der Objektrelationen besser einzugrenzen, können JOIN-Anweisungen eingesetzt werden. Das Verfahren arbeitet in vergleichbarer Weise zu SQL-Joins. Die vier möglichen JOIN-Anweisungen lassen sich unterteilen nach

  • Ref: mit Zielstatement-Angabe; JOIN zwischen den beiden Objekttypen des Statements und Zielstatements per referenzierendem Constraint, oder PK-Constraint bei Self-JOINs, oder ohne Constraint bei globalen JOINs.
  • NoRef: ohne Zielstatement-Angabe; JOIN zwischen Statement-Objekttyp und referenzierendem Objekttyp per Foreign-Key-Constraint. Hier sind mehrere (und nur) FK-Constraints möglich, die jeweils mit and oder or bewertet werden. Entspricht einer exists bzw. not exists-Abfrage (zum Statement-Objekt gehören bzw. gehören keine Objekte des referenzierenden Objekttyps)
  • Semi/Inner: Übereinstimmung der Referenzattribute
  • Anti: Nicht-Übereinstimmung der Referenzattribute
Ref-InnerJOIN-Anweisung

Die Ausgabe eines Statements (‘Nachfolger’) wird mit der Ausgabe eines oder mehrerer (direkt oder indirekt) vorausgehenden Statements (‘Vorgänger’) verbunden.

Verbund bedeutet dabei, dass zwischen den Objekttypen dieser beiden Statements eine Relation in Form eines referenzierenden Constraints besteht. Dabei werden nur diejenigen Objekte des Nachfolger-Statements ausgegeben, deren referenzierbare (bzw. referenzierte) Attributwerte zu denen des Vorgänger-Statements passen. Die Ausgabe des Vorgänger-Statements wird durch die JOIN-Anweisung somit nicht beeinflusst. Der Verbund wird dabei über den Index des Vorgänger-Statements und -außer im Globalverbund- den Namen des referenzierenden Constraints (s. Abschnitt Objekttypen) definiert. Das Nachfolger-Statement hat dabei einen Index > 0, und dessen Funktion ist nicht datenmodifizierend. Der Index des Vorgänger-Statements ist kleiner als der des Nachfolger-Statements. In der Ref-InnerJOIN-Anweisung (JSON-Dict unter inner_join_ref) ist <ref_sidx> (Schlüssel) der Index des Vorgänger-Statements, und "<referencing_constraint_name>" (Wert zum Schlüssel) entweder der Constraint-Name, das Schlüsselwort "default", "self" oder der Wert null, je nach Variante. Alle diese Schlüssel-Wert-Paare werden funktional logisch-UND verknüpft.

Es gibt 2 Verbundvarianten:

  1. Constraint-basiert: Zwischen den Objekttypen des Vorgänger- und Nachfolger-Statements existiert ein referenzierender Constraint, der in der jeweiligen Objekttypdefinition unter referencing (seitens des Child-Objekttyps) oder referenced_by (seitens des Parent-Objekttyps) ausgewiesen ist. Der Constraint gehört somit entweder zum Objekttyp des Vorgänger- oder des Nachfolger-Statements; die Reihenfolge beider Statements kann also nach Bedarf frei gewählt werden. Wenn es zwischen beiden Objekttypen mehrere referenzierende Constraints gibt, und der gewünschte (einzusetzende) Constraint mit dem Attribut "is_join_default": false ausgewiesen ist, muss dessen Name in der JOIN-Anweisung unter inner_join_ref oder anti_join_ref als Wert für "<referencing_constraint_name>" (zum Dict-Schlüssel <ref_sidx>) angegeben werden. Andernfalls ("is_join_default": true) kann dessen Name durch das Schlüsselwort "default" ersetzt werden.

    Im Nachfolger-Statement werden dadurch nur Objektdatensätze ausgegeben, die entsprechend des angegebenen Constraints zu Objekten des Vorgänger-Statements gehören. Die Zugehörigkeit der Objektdatensätze dieser beiden Verbund-Statements wird je nach JOIN-Operator über die Wertegleichheit/Zuordbarkeit der jeweiligen (Objekttyp-)Attribute des referenzierenden Constraints und des referenzierten Constraints hergestellt. (s.a. Beispiel)

    Lesezugriff des Nachfolger-Statements: Hat der eingesetzte Constraint das Attribut

    grants_read_access = true,
    

    wird der bestehende Lesezugriff für Objekte des Vorgänger-Statements automatisch auf die zugehörigen Objekte des Nachfolger-Statements übertragen. Dadurch wird garantiert, dass letztere auch ausgegeben werden, obwohl bei deren direkter Abfrage (ohne JOIN) kein Lesezugriff bestanden hätte.

    Self-Joins: Die Objekttypen des Vorgänger- und Nachfolger-Statements sind identisch und können mittels PK-Constraint oder, falls vorhanden, mittels FK-Constraint verbunden werden.

    • Bei Angabe des Schlüsselworts "self" als Constraintname erfolgt der JOIN mittels PK-Constraint. Der bestehende Lesezugriff für Objekte des Vorgänger-Statements wird automatisch auf die zugehörigen Objekte des Nachfolger-Statements übertragen.
    • Bei Angabe eines referenzierenden Constraints wird dieser immer dem Nachfolger-Statement zugeordnet, d.h. Nachfolger referenziert Vorgänger. Es können somit im Nachfolger-Statement nur Child-Datensätze des Vorgänger-Statements ausgegeben werden. Eine Umkehrung (Nachfolger ist Parent zu Vorgänger bzw. Nachfolger wird durch Vorgänger referenziert) ist hier nicht möglich.
  2. Global: Der Wert zum Dict-Schlüssel <ref_sidx> ist null, da es in den beiden folgenden Varianten keinen Constraint zwischen den Objekttypen des Vorgänger- und Nachfolger-Statements gibt. Hintergrund ist, dass ein Objekttyp der Kategorie “Datensenke” (Child) mehrere Objekttypen der Kategorie “Datenquelle” (Parent) referenziert. Diese Referenzen können daher nicht statisch als Constraint hinterlegt werden, sondern müssen dynamisch (zur Laufzeit) berechnet werden.

    Die Varianten unterscheiden sich nach der Reihenfolge der Statements bzgl. der Kategorien der Objekttypen “Datensenke” (DS, immer Child) oder “Datenquelle” (DQ, immer Parent). Als Datenquelle werden Objekttypen bezeichnet, die Log- oder OT-Attributdaten zuordbar sind; als Datensenke solche, die die eigentlichen Log- oder OT-Attributdaten enthalten.

    Im Nachfolger-Statement werden alle Datensätze ausgegeben, die entsprechend der übereinstimmenden Datenart (z.B. Log, OT-Attribut) zu Objekten des Vorgänger-Statements gehören. Die Zugehörigkeit der Objektdatensätze dieser beiden Verbund-Statements wird über die Wertegleichheit des Attributs object_gfk (DS-seitig) und des Attributs gpk (DQ-seitig) hergestellt.

    1. Child joins Parent:

      Der Objekttyp des Vorgänger-Statements (Parent) gehört zur Kategorie “Datenquelle”, und der Objekttyp des Nachfolger-Statements (Child) gehört zur Kategorie “Datensenke”. Diese Variante ist geeignet, um DQ-seitig nach Objekten zu suchen sowie deren DS-seitig zugeordnete Objekte (Log-Daten bzw. OT-Attributwert-Daten) auszugeben.

      Lesezugriff des Nachfolger-Statements (Child/DS): Dieser wird automatisch von Objekten des Vorgänger-Statements auf die zugehörigen Objekte des Nachfolger-Statements vererbt und verhält sich wie oben bei Constraint-basierten Joins mit

      grants_read_access = true.
      

      Beispiele:

      • zu gefundenen Objekten (DQ) die zugehörigen Log-Einträge (z.B. nach Top-N) anzeigen
      • zu gefundenen Objekten (DQ) die zugehörigen Objektattribute (z.B. nach Attributschlüssel oder -wert) anzeigen
    2. Parent joins Child: Der Objekttyp des Vorgänger-Statements (Child) gehört zur Kategorie “Datensenke”, und der Objekttyp des Nachfolger-Statements (Parent) gehört zur Kategorie “Datenquelle”. Diese Variante ist geeignet, um DS-seitig nach Objekten (Log-Daten bzw. OT-Attributwert-Daten) zu suchen und deren DQ-seitig zugeordnete Objekte auszugeben.

      Lesezugriff des Vorgänger-Statements (Child/DS): Wenn durch die Parameterbelegung der Abfrage im Vorgänger-Statement sichergestellt ist, dass diese auf Objekte genau eines Objekttyps begrenzt wird, wird der Lesezugriff für Objekte des Nachfolger-Statements automatisch auf die zugehörigen Objekte des Vorgänger-Statements angewendet. Ansonsten ist für den Objekttyp des Vorgänger-Statements expliziter Lesezugriff erforderlich.

      Beispiele:

      • welche der gefundenen Log-Einträge (z.B. nach Zeitraum oder Kategorie oder User) sind welchen Objekten (DQ) zugeordnet
      • welche der gefundenen Attribute (z.B. nach Attributschlüssel oder -wert) sind welchen Objekten (DQ) zugeordnet
Ref-AntiJOIN-Anweisung

Wirkt analog zur Ref-InnerJOIN-Anweisung, mit folgenden Ausnahmen:

  • Im Nachfolger-Statement werden nur Objektdatensätze ausgegeben, für die keine zugehörigen Objekte des Vorgänger-Statements existieren
  • Globale JOINs sind nicht möglich, d.h. in der Ref-AntiJOIN-Anweisung (JSON-Dict unter anti_join_ref) ist <ref_sidx> (Schlüssel) der Index des Vorgänger-Statements, und "<referencing_constraint_name>" (Wert zum Schlüssel) entweder der Constraint-Name, das Schlüsselwort "default" oder "self".
  • Automatisch übertragener Lesezugriff wie in der Ref-InnerJOIN-Anweisung (über das Constraint-Attribut grants_read_access bzw. über Self-Joins) wird nicht gewährt. Es ist also immer expliziter Lesezugriff erforderlich.
NoRef-SemiJOIN-Anweisung

Im Gegensatz zu den RefJOINs wird kein Ziel-Statement benötigt. Als Constraints sind nur FK-Constraints erlaubt, die den Objekttyp des aktuellen Statements referenzieren. Es werden nur Objektdatensätze ausgegeben, wenn zugehörige Objekte der referenzierenden Objekttypen existieren (entsprechend der angegebenen Boolean-Vernküpfung). NoRef-JOINs wirken daher wie eine zusätzliche Bedingung zur Eingrenzung der abgefragten Datensätze.

NoRef-AntiJOIN-Anweisung

Wirkt analog zur NoRef-SemiJOIN-Anweisung, außer dass nur Objektdatensätze ausgegeben werden, für die keine zugehörigen Objekte der referenzierenden Objekttypen existieren (entsprechend der angegebenen Boolean-Vernküpfung).

Beispiel

Bedingte Ausführung eines Statements mit der WHEN-Anweisung

Statements, die nur unter bestimmten Voraussetzungen ausgeführt werden sollen, können durch die WHEN-Anweisung gesteuert werden. In der WHEN-Anweisung lassen sich boolean-verknüpfte Bedingungen formulieren, die von den Ergebnisdaten eines oder mehrerer Vorgänger-Statements abhängen. Durch die Kombination datenmodifizierender Statements mit gezielt konstruierten list-Abfragen (hier könnte auch die Verwendung von fetch_limit, fetch_offset, sorting_params_list besonders hilfreich sein) lassen sich auf diese Weise Vorgänge nach einer Semantik wie z.B. create_if_not_exists oder delete_if_exists realisieren.

Die WHEN-Anweisung besteht entweder aus einem JSON-Boolean-Wert (true oder false) oder aus einem JSON-Dict mit den folgenden Besonderheiten:

  • Das JSON-Dict stellt den äußeren Funktionsaufruf dar und hat genau einen Schlüssel. Diese äußere Funktion muss den Datentyp boolean zurückgeben.
  • Der Schlüssel ist der Funktionsname, und der Wert ist ein JSON-Array, das die Funktionsargumente enthält. Die Boolean-Basisfunktionen and, or, not werden hier in der Präfixnotation verwendet, d.h. true or false wird als or(true, false) dargestellt. Während and und or mindestens 1 Argument haben, hat not genau 1 Argument.
  • Ein Funktionsargument kann wiederum ein JSON-Dict mit den gleichen Eigenschaften wie das äußere JSON-Dict sein (innere Funktion); oder konstante JSON-Daten der verbleibenden JSON-Typen ‘array’, ‘string’, ‘number’, ‘boolean’, ‘null’, je nach Kontext.
  • Der Rückgabe-Datentyp der gerufenen Funktion muss mit dem Datentyp des positionalen Arguments der rufenden Funktion übereinstimmen.

In der folgenden Tabelle sind die verwendbaren Funktionen (Dict-Schlüssel) und deren Argumente zusammengestellt.

Name (Dict-Schlüssel) Return-Datentyp Anzahl Argumente Position und Datentyp der Argumente Argument-Inhalt
"and" boolean > 1 boolean
...
"or" boolean > 1 boolean
...
"not" boolean 1 boolean
"executes" boolean 1 json: string Statement-Index
"returns_data" boolean 1 json: string Statement-Index
"returns_no_data" boolean 1 json: string Statement-Index
"compare" boolean 3 oder 4 json: string <operator_name>
intern linker Operand
json rechter Operand
json Bereichs-Operand (Standard: null)
"compare" boolean 4 json: string <operator_name>
intern linker Operand
json rechter Operand
intern Bereichs-Operand
"compare" boolean 3 oder 4 json: string <operator_name>
json linker Operand
intern rechter Operand
json Bereichs-Operand (Standard: null)
"compare" boolean 4 json: string <operator_name>
json linker Operand
intern rechter Operand
intern Bereichs-Operand
"compare" boolean 3 oder 4 json: string <operator_name>
intern linker Operand
intern rechter Operand
json Bereichs-Operand (Standard: null)
"compare" boolean 4 json: string <operator_name>
intern linker Operand
intern rechter Operand
intern Bereichs-Operand
"compare" boolean 3 json: string <operator_name>
json linker Operand
json rechter Operand
"returned_row_count" intern: integer 1 json: string Statement-Index
"returned_param_value" intern 2 oder 3 json: string Statement-Index
json: string <parameter_name>
json: number <object_dict_pos> (Standard: 0)
"returned_param_value_list" intern 2 oder 3 json: string Statement-Index
json: string <parameter_name>
json: array, null <object_dict_pos_array> (Standard: null)
"returned_param_value_list" intern 2 oder 3 json: string Statement-Index
json: string <parameter_name>
json: string, null <object_dict_pos_range_literal> (Standard: null)

Erläuterungen

  • <parameter_name>: unqualifizierter Name des Attributes, das zum Objekttyp des Statements am Statement-Index gehören muss.
  • <operator_name>: Name des Operators aus cntl.data_type_operator.name. Die Datentypen der Operanden sind in cntl.data_type_operator.description verbal beschrieben.
  • <object_dict_pos>: Dict-Position des Ergebnisdatensatzes (Object-Dict) des Bezugsstatements.
    • Die Position ist 0-basiert; d.h. das erste Dict des Bezugsstatements hat die Position 0.
    • Negative Werte adressieren die Position vom letzten Dict aus gerechnet; d.h. das letzte Dict hat die Position -1, das vorletzte -2 usw.
    • Nicht-existierende Positionen bewirken die Rückgabe von NULL.
  • <object_dict_pos_array>: Einzelwerte als JSON-Array der Dict-Positionen (nicht zwingend aufeinanderfolgend)
    • Positionswerte wie <object_dict_pos>
    • null gibt die Werte aller Positionen zurück (äquivalent zu [0,-1])
  • <object_dict_pos_range_literal>: Positionsbereich (durchgehend) als Literal im Format "[lower_bound:upper_bound]", analog zur Array-Slice-Notation.
    • Sowohl lower_bound als auch upper_bound dürfen leer sein, wobei dann jeweils die erste oder letzte Dict-Position gilt.
    • Negative Werte adressieren die Position vom letzten Dict aus gerechnet; d.h. das letzte Dict kann durch die Position -1, das vorletzte durch -2 (usw.) angesprochen werden.
    • null gibt die Werte aller Positionen zurück (äquivalent zu "[:]" oder "[0:-1]")
  • Der Datentyp ‘intern’ liefert je nach Kontext den Datentyp des Objekt-Attributs (Parameters). Dies muss bei der Kombination mit dem passenden Operator beachtet werden.
  • Die Boolean-Logik funktioniert gemäß PostgreSQL-Dokumentation.

Beispiele

Wiederholte Ausführung eines Statements mit der RefParams-Anweisung

Aus den JSON-Dicts (RefParams-Dicts) der RefParams-Anweisung (äußere RefParams-Arrays unter "old_ref_params" bzw. "new_ref_params") wird eine interne SQL-Abfrage konstruiert, deren Ergebnisdatensätze als Laufzeit-Parameter in einer Schleife (Loop) an die formalen Funktionsparameter des Statements übergeben werden. Bei den Funktionen insert oder delete wird nur die jeweils zutreffende der RefParams-Anweisung benötigt; bei update können beide verwendet werden. Im letzten Fall werden die Ergebnisdatensätze beider Anweisungen durchlaufen. Gibt eine Anweisung weniger Datensätze zurück als die andere, werden Parameterwerte der fehlenden Datensätze mit NULL bzw. dem Defaultwert, falls vorhanden, aufgefüllt.

Jedes RefParams-Dict entspricht einem Ziel- bzw. Referenzobjekttyp, adressiert durch den Statement-Index "idx": "<ref_sidx>". Ist der JOIN-Typ "join_type": null, handelt es sich um das Basis-Statement. Die Referenzobjekttypen aller RefParams-Dicts bilden dabei einen SQL-JOIN, der nach einem einfachen Stern-Schema aufgebaut ist. Dessen Mittelpunkt ist immer der Objekttyp des Basis-Statements.

Für die Bildung der Laufzeit-Parameter ist das Parameter-Mapping entscheidend. Gleichnamige lokale Parameter werden entsprechend der Position des RefParams-Dicts priorisiert, wobei die höchste Position die höchste Priorität hat. Ist ein Parameterwert NULL (nur auf SQL-Ebene, nicht auf JSON-Ebene), wird der erste nicht-null-Wert aus den vorausgehenden Positionen eingesetzt (SQL-NULL entsteht nur bei outer-JOIN-Typen). Sind die Werte aller vorausgehenden Positionen ebenfalls NULL, wird der statische Parameterwert (Altwert/Neuwert durch old/new) eingesetzt.

Die Schleife (und damit die Funktion des aktuellen Statements) durchläuft alle Datensätze, die durch die konstruierte Abfrage zurückgegeben werden. Im einfachsten Fall sind sie identisch mit der Ausgabe eines vorigen (des referenzierten) Statements; im komplexeren Fall werden die Ausgaben verschiedener referenzierter Statements über die JOIN-Bedingung (Werte- oder Positionsgleichheit) mit dem Basis-Statement verbunden (RefParam-JOIN) und deren Parameterwerte entsprechend der JOIN-Reihenfolge priorisiert.

Die Sortierung der Loop-Datensätze entspricht der positionalen Reihenfolge der Ergebnisdatensätze aus den referenzierten Statements, wobei zuerst die Daten des Basis-Statements (unabhängig von seiner Position im RefParams-Dict) gewertet werden, danach die der übrigen Statements nach deren aufsteigender Position im RefParams-Dict.

Die Struktur eines RefParams-Dicts:

{
  "idx": "<ref_sidx>",
  "params": {"<param_name>": "<ref_param_name>", ...},
  "pos": null | [0,1,-1] | "<pos_range_literal>",
  "join_type": null | "<join_type_keyword>",
  "join_on": "pos" | "val",
  "join_offset_by_idx_list": ["<ref_sidx>", ...],
  "join_offset_by_constant": 0
}

Erläuterungen (s.a. Schlüssel transaction_json_schema im Versionsindex):

  • "idx": Index des referenzierten Statements
  • "params": Dict des Parameter-Mappings. Schlüssel ("<param_name>") sind die unqualifizierten Parameternamen des lokalen Objekttyps, Werte ("<ref_param_name>") die des referenzierten Objekttyps.
  • "pos": positionale Bereichsangabe der Ergebnisdatensätze des referenzierten Statements. "<pos_range_literal>": analog "<object_dict_pos_range_literal>" aus der WHEN-Anweisung
  • "join_type": JOIN-Typ, entspricht SQL-JOIN-Typen. null steht für das Basis-Statement und muss genau einmal in allen RefParams-Dicts des aktuellen Statements vorkommen. Mögliche Werte sind:

    • null
    • "inner"
    • "cross"
    • "left_outer"
    • "right_outer"
    • "full_outer"
    • "left_anti"
    • "right_anti"
    • "full_anti"
  • "join_on": JOIN-Bedingung; bewertet Positionsgleichheit (Standard) oder Wertegleichheit gegen die Ergebnisdaten des Basis-Statements. Der Wertevergleich erfolgt als Tupel-Vergleich entsprechend der Tuple-Definition in ref_params_join_on_val_attrs_tuple der beiden beteiligten Statements. Das Wertetupel ist ein JSON-Array, das die Attributwerte in gleicher Reihenfolge enthält.

  • "join_offset_by_idx_list": Array aus Indizes der referenzierten Statements; nur bei Positionsgleichheit. Deren Anzahl der Ergebnisdatensätze wird summiert, ggf. mit dem Wert aus join_offset_by_constant addiert und als positionaler Offset des referenzierten Statements in die JOIN-Bedingung eingerechnet. Für den Einsatz bei outer-JOIN-Typen vorgesehen.
  • "join_offset_by_constant": konstanter positionaler Offset; nur bei Positionsgleichheit. Wird zum Ergebniswert aus join_offset_by_idx_list (falls angegeben) addiert, ansonsten direkt als positionaler Offset des referenzierten Statements in die JOIN-Bedingung eingerechnet. Für den Einsatz bei outer-JOIN-Typen vorgesehen.

Generischer Objekttyp

Mit dem generischen Objekttyp tmp.generic_object lassen sich willkürlich definierte Daten in die Transaktionsumgebung importieren (in Kombination mit der RefParams-Anweisung sinnvoll), oder auch Parameterbelegungen zur Laufzeit simulieren und testen. Im Prinzip handelt es sich hier nur um eine einfache Echo-Funktion, die die übergebenen Daten als Antwort zurückgibt. Dabei werden keine persistenten Daten in die Datenbank geschrieben.

Bei diesem Objekttyp gibt es im Gegensatz zu regulären Objekttypen nur eine statische Parameterdefinition:

  • _dict für die Funktionen create und update: Definition des (genau einen) Ergebnisdatensatzes (neu) als JSON-Dict.
  • _dict_list für die Funktion list: Definition der Ergebnisdatensätze (alt) als JSON-Array aus Dicts.

Unterhalb dieser Ebene können die eigentlichen Nutzdaten (Ergebnisdatensätze) definiert werden. Das Parameter-Mapping der RefParams-Anweisung übernimmt diese Umstetzung automatisch, nicht aber die Vorbelegung mittels old oder new. Soll z.B. ein regulärer Objekttyp gegen den generischen Objekttyp in einem Transaktions-JSON-Dokument getauscht werden, müssen die Parameter-Dicts des regulären Objekttyps als Parameterwert für _dict bzw. _dict_list des generischen Objekttyps eingesetzt werden. Schema der Substitution:

  • regulärer Objekttyp:
[
  ...,
  {
    "title": "create an object of regular type",
    "name": "dns.fqdn.create",
    "new": { "value": "a.b.c", "type": "domain" }
  },
  ...
]
  • generischer Objekttyp:
[
  ...,
  {
    "title": "pseudo-create an object of generic type",
    "name": "tmp.generic_object.create",
    "new": { "_dict": { "value": "a.b.c", "type": "domain" } }
  },
  ...
]

Das Parameter-Mapping in der RefParams-Anweisung: fqdn aus Statement defineData wird nach value in Statement modifyData umgesetzt. Die Umsetzung der _dict_list-Ebene ist hier transparent.

[
  {
    "title": "Definition of 3 FQDNs",
    "idx": "defineData",
    "name": "tmp.generic_object.list",
    "old": { "_dict_list": [ { "fqdn": "a.b.c" }, { "fqdn": "d.e.f" }, { "fqdn": "g.h.i" } ] }
  },
  {
    "title": "Create the defined FQDNs. All FQDNs are of type 'domain'.",
    "idx": "modifyData",
    "name": "dns.fqdn.create",
    "new": { "type": "domain" },
    "new_ref_params": [ { "idx": "defineData", "params": { "value": "fqdn" } } ]
  }
]

Unter Hinzunahme obiger Substitution zum Testen der Parameterwerte für dns.fqdn.create via tmp.generic_object.create erhalten wir:

[
  {
    "title": "Definition of 3 FQDNs",
    "idx": "defineData",
    "name": "tmp.generic_object.list",
    "old": { "_dict_list": [ { "fqdn": "a.b.c" }, { "fqdn": "d.e.f" }, { "fqdn": "g.h.i" } ] }
  },
  {
    "title": "Pseudo-create the defined FQDNs to see the resulting parameter values for each function call.",
    "idx": "modifyData",
    "name": "tmp.generic_object.create",
    "new": { "_dict": { "type": "domain" } },
    "new_ref_params": [ { "idx": "defineData", "params": { "value": "fqdn" } } ]
  }
]

Beispiele

GET-Request

Diese Methode kann nur für nicht-datenmodifizierende Funktionen als Einzelbefehls-Aufruf verwendet werden. Im Funktionsindex sind diese durch das Attribut is_data_manipulating mit dem Wert false gekennzeichnet. Die Parameterzeichenkette (query string) entspricht hierbei der Belegung unter old im Parameterverzeichnis des Funktionsindex. Da die Parameterzeichenkette kein JSON-Format hat, werden die Parameterwerte nach JSON konvertiert; d.h. der Parameterwert wird als JSON-Literal betrachtet und daraus der entsprechende JSON-Datentyp abgeleitet. Dabei ergeben sich folgende Besonderheiten:

  • Parameterwerte der JSON-Datentypen string oder number werden gleichwertig betrachtet
  • Parameterwerte des JSON-Datentyps boolean sind als Zeichenkette true oder false zu übergeben
  • null kann nicht verwendet werden, da es als eigener JSON-Datentyp existiert und keine Unterscheidung zum Typ string möglich ist

Alle Parameterwerte haben durchweg auswählenden Charakter und werden typischerweise logisch-UND verknüpft. Elemente in Parameterwerten des JSON-Datentyps array werden typischerweise logisch-ODER verknüpft. Parameternamen dürfen nur einmal in der Parameterzeichenkette vorkommen; andernfalls werden deren Werte als Array gepackt übergeben und dadurch fälschlicherweise als Array-Literal interpretiert, was zu unerwünschten Ergebnissen oder zur Ablehnung des Requests führen kann. Parameterwerte, die reservierte URI-Zeichen enthalten, müssen kodiert werden (Stichworte ‘Prozentkodierung’, ‘URL-Encoding’, s.a. https://de.wikipedia.org/wiki/URL-Encoding).

Antwort-Datenstruktur bei fehlerfreier Transaktion

Die Antwort ist immer ein JSON-Dokument mit dem Aufbau (von außen nach innen) in den folgenden 2 Varianten, je nachdem, wie dict_mode gesetzt ist:

  1. dict_mode = false (Standard):

    • JSON-Array: Transaktionsebene; enthält die Statement-Ebene mit den JSON-Arrays der Funktions-Antworten in der Reihenfolge der Statements aus dem Request Body. Die Statement-Position ist immer 0-basiert, d.h. das erste Statement hat die Position 0 (und, falls nicht vorgegeben, den Index “0”).
    • JSON-Array: Statement-Ebene; enthält die Objektebene mit den JSON-Dicts der Funktions-Antwort
    • JSON-Dict: Ergebnisdatensatz (Objekt), dessen Schlüssel bzw. Keys gleichlautend zu den Attributnamen (aus der Objekttyp-Struktur) sind
  2. dict_mode = true:

    • JSON-Dict: Transaktionsebene; enthält die Statement-Ebene mit den JSON-Arrays der Funktions-Antworten. Die Statements der Transaktionsebene sind entsprechend des Statement-Index des Request Body indiziert.
    • JSON-Array: Statement-Ebene; enthält die Objektebene mit den JSON-Dicts der Funktions-Antwort
    • JSON-Dict: Ergebnisdatensatz (Objekt), dessen Schlüssel bzw. Keys gleichlautend zu den Attributnamen (aus der Objekttyp-Struktur) sind

Antwort-Datenstruktur im Fehlerfall (Exception, Transaktionsabbruch)

Zusätzlich zum HTTP-Statuscode (4xx) wird ein JSON-Dict zurückgegeben, dessen (einziger) Key exception wiederum ein Dict mit der Struktur des Objekttyps wapi.exception enthält. Muster:

{
  "exception": {
    "error": {
      "code": number,
      "description": "text",
      "details": "text"
    },
    "error_type": {
      "code": number,
      "name": "text",
      "description": "text"
    },
    "constraint": {
      "name": "text",
      "description": "text"
    },
    "hint": "text",
    "others": {},
    "stacked_diag_params" {
      "column": "text",
      "constraint": "text",
      "context": "text",
      "datatype": "text",
      "detail": "text",
      "dml_src_table": "text",
      "hint": "text",
      "message": "text",
      "schema": "text",
      "sqlstate": "text",
      "table": "text"
    },
    "traceback": [
      {
        "function": "text",
        "param": {
          "<param_name1>": [ { "state": "state_descr", "value": <param_value1>, ... } ],
          "<param_name2>": [ { "state": "state_descr", "value": <param_value2>, ... } ],
          ...
        }
      },
      ...,
      {
        "function": "wapi_4_0.exec_ta_handler",
        "param": {
          "wapi.transaction_stmt.index": number
        }
      }
    ]
  }
}
  • Die Keys der JSON-Dicts error und error_type entsprechen den gleichnamigen Objekttypen und deren Attribute im System cntl.
  • stacked_diag_params enthält interne Informationen zur Fehlerbehebung (Debugging).
  • constraint mit name und description sagt verbal aus, gegen welche Bedingung verstoßen wurde.
  • traceback enthält eine Liste (JSON-Array) entsprechend der Funktionsaufruffolge und der jeweiligen Parameternbelegung mit Parametername und -Wert. Der letzte Eintrag wapi.transaction_stmt.index sagt aus, welches Statement der Transaktion die Exception verursacht hat.
  • others ist nur im Fall nicht-zuordbarer Fehler (unbehandelte Exceptions) belegt.

Indexabfragen

Die Indexabfragen erlauben es, die gültigen Wertebelegungen der jeweils nächsten Unterebene des URI-Schemas festzustellen. Optional kann der endende Schrägstrich / (Slash) angehängt werden, optional gefolgt vom Schlüsselwort index; dies hat aber keine Auswirkungen und dient nur der Rückwärtskompatibilität bzw. Lesbarkeit. Indexabfragen sind parameterlos. Intern sind sie eine Weiterleitung auf den entsprechenden Objekttyp im Systemdatenbereich unter /wapi.

Für die 4 Unterebenen gilt folgendes allgemeines Schema:

Versionsindex : <BASE-URI>
Systemindex   : <BASE-URI>/<vmajor>.<vminor>
Objekttypindex: <BASE-URI>/<vmajor>.<vminor>/<sys>
Funktionsindex: <BASE-URI>/<vmajor>.<vminor>/<sys>/<objtype>

Dazu die äquivalenten Systemdatenabfragen entsprechend der internen Weiterleitung (außer Versionsindex):

Systemindex   : <BASE-URI>/<vmajor>.<vminor>/wapi/system/list
Objekttypindex: <BASE-URI>/<vmajor>.<vminor>/wapi/object_type/list?system_list=["<sys>"]
Funktionsindex: <BASE-URI>/<vmajor>.<vminor>/wapi/function/list?system_list=["<sys>"]&object_type_list=["<objtype>"]

Beispiele:

Abfragen der Systemdaten

Für den Systemdatenbereich ist das Spezial-System /wapi vorgesehen. Zugriffe auf diesen Bereich dürfen auch ohne Authentifizierung erfolgen. Unterhalb dieses Systems sind Basis-Objekttypen für

  • Systeme
  • Objekttypen
  • Funktionen und deren Zuordnungen zu Parametern bzw. Objekttyp-Attributen
  • Objekttyp für Transaktionsaufrufe
  • Objekttyp für Exceptions

definiert. Diese liefern Informationen über alle weiteren Systeme, Objekttypen und Funktionen.

Allgemeine Objekttyp-Datenstruktur

Jeder Objekttyp wird nach folgendem Schema dargestellt:

{ 
  "attributes": {
    "<attribute_name>": {
      "data_type": "<api_datatype_name>",
      "description_detail": "detaillierter Beschreibungstext des Attributes",
      "description_obj_type_scope": "objekttyp-weite Beschreibung des Attributes",
      "description_sys_scope": "system-weite Beschreibung des Attributes",
      "is_core": true | false,
      "is_deprecated": true | false,
      "is_nullable": true | false,
      "supported_values": {
        "<value>": "<value_description>"
      }
    }
  },
  "constraints": {
    "<constraint_name>": {
      "attributes": [ "<attribute_name>" ],
      "description": "Beschreibungstext des Constraints",
      "errors": [
        {
          "code": <error_code>,
          "type": <error_type_code>,
          "description": "<error_description>",
          "details": "<error_details>"
        }
      ],
      "grants_read_access": true | false,
      "internal_name": "systeminterner Name des Constraints",
      "is_deferred": true | false,
      "type": "<constraint_type>"
    }
  },
  "description_abbrev": "Abkürzung des Objekttyps",
  "description_detail": "ausführlicher Beschreibungstext des Objekttyps",
  "description_title": "Titeltext des Objekttyps",
  "do_activate_global_pk2obj": true | false,
  "fq_name": "<system_name>.<object_type_name>",
  "is_lang_dst": true | false,
  "is_lang_src": true | false,
  "is_log_dst": true | false,
  "is_log_src": true | false,
  "is_otattr_dst": true | false,
  "is_otattr_src": true | false,
  "name": "<object_type_name>",
  "object_type_grants_read_access": true | false,
  "referenceable": {
    "<constraint_name>": {
      "attributes": [ "<attribute_name>" ],
      "is_deferred": true | false,
      "referenced_by": [ {"system": "<system_name>", "object_type": "<object_type_name>", "name": "<constraint_name>", "is_join_default": true | false} ],
      "type": "<constraint_type>"
    }
  },
  "referencing": {
    "<constraint_name>": {
      "attributes": [ "<attribute_name>" ],
      "grants_read_access": true | false,
      "is_deferred": true | false,
      "is_join_default": true | false,
      "on_delete": "raise" | "cascade" | "set null" | "set default",
      "references": {"system": "<system_name>", "object_type": "<object_type_name>", "name": "<constraint_name>"}
    }
  },
  "system": "<system_name>"
}

Bedeutung der Platzhalter (Variablen)

  • <system_name>: System-Name
  • <object_type_name>: Objekttyp-Name
  • <attribute_name>: Attribut-Name
  • <api_datatype_name>: Datentyp-Name des Attributs (Name auf API-Ebene)
  • <json_datatype_name>: Datentyp-Name des Attributs (Name auf JSON-Ebene), Wertebereich:
    • object
    • array
    • string
    • number
    • boolean
    • json (allgemein; kann ein beliebiger der vorgenannten JSON-Datentypen sein)
  • <constraint_name>: Constraint-Name
  • <constraint_type>: Constraint-Typ
  • <value>: erlaubter Attributwert
  • <value_description>: Erläuterung zu diesem Wert
  • <error_code>: numerischer Fehlercode
  • <error_type_code>: numerischer Fehlertypcode (negativ)
  • <error_description>: Fehlerbeschreibungstext
  • <error_details>: detaillierter Fehlertext

Bedeutung der Bezeichner (Konstanten)

  • “system”: Name des Systems, dem der Objekttyp zugeordnet ist
  • “name”: Objekttypname innerhalb eines Systems
  • “fq_name”: voll qualifizierter (system-qualifizierter) Objekttypname
  • “is_lang_dst”: Objekttyp ist OT-Sprachattribut-Datensenke (DS; hält die Inhalte (Texte) relationaler Objektsprachattribute)
  • “is_lang_src”: Objekttyp ist OT-Sprachattribut-Datenquelle (DQ; objektseitiger Ursprung relationaler Objekttypsprachattribute)
  • “is_log_dst”: Objekttyp ist Log-Datensenke (DS; hält die Inhalte der Log-Daten)
  • “is_log_src”: Objekttyp ist Log-Datenquelle (DQ; objektseitiger Ursprung der Log-Daten, welche in der Log-Datensenke abgelegt werden)
  • “is_otattr_dst”: Objekttyp ist OT-Attribut-Datensenke (DS; hält die Inhalte (Werte) relationaler Objektattribute)
  • “is_otattr_src”: Objekttyp ist OT-Attribut-Datenquelle (DQ; objektseitiger Ursprung relationaler Objekttypattribute)
  • “do_activate_global_pk2obj”: globaler Primärschlüssel ist aktiv
  • “object_type_grants_read_access”: Lesezugriff auf Objekte dieses Typs gewährt
  • “description_abbrev”: Abkürzung des Objekttyps
  • “description_detail”: ausführlicher Beschreibungstext des Objekttyps
  • “description_title”: Titeltext des Objekttyps
  • “constraints”: Constraints (Datenintegritäts- und Prüfbedingungen) des Objekttyps und ggf. zugeordnete Fehlermeldungen.

    • “type”: Constraint-Typ. Wertebereich:
      • “U”: Schlüssel/Unique Key, referenzierbar. Es ist garantiert, dass die enthaltenen Attribute genau ein Objekt unter allen Objekten desselben Typs identifizieren. Darüber kann dieses Objekt angesprochen bzw. referenziert werden.
      • “UP”: wie “U”, enthält die veränderlichen Primärattribute des Objekts (existiert genau einmal pro Objekttyp). Unterscheidungsmerkmal gegenüber “P”: die UP-Attribute sind veränderlich.
      • “P”: Primärschlüssel/Primary Key (wie “U”, existiert genau einmal pro Objekttyp). Die Attribute eines Primärschlüssels sind garantiert unveränderlich, d.h. solange ein Objekt existiert, wird sich sein Primärschlüssel nicht ändern. (Unterscheidungsmerkmal gegenüber “UP”)
      • “X”: Ausschlußbedingung zur Verhinderung von Duplikaten unter bestimmten Bedingungen
      • “F”: Fremdschlüssel/Foreign Key, referenzierend, Merkmal der referentiellen Datenintegrität. Die enthaltenen Attribute sind eine Bezugnahme zu denen im referenzierten Schlüssel oder Primärschlüssel. Dabei ist garantiert, dass zu einem referenzierenden Objekt (child) ein referenziertes Objekt (parent) existiert. Der Bezug wird über die jeweilige Reihenfolge der Attribute hergestellt, d.h. das enthaltene (referenzierende) Attribut an einer Position ist dem referenzierten Attribut (des Schlüssels oder Primärschlüssels) an derselben Position zugeordnet. Beide Attribute sind dabei wertgleich und nicht null. Ist ein Attribut null, gilt die garantierte Bezugnahme nicht.
      • “FN”: Funktionsbasierte Referenz, referenzierend (aber kein Merkmal der referentiellen Datenintegrität) Die enthaltenen Attribute sind eine funktionale Bezugnahme zu denen im referenzierten Schlüssel oder Primärschlüssel.
      • “C”: einfache Prüfbedingung
      • “T”: Trigger (Komplexeres Prüfprogramm)
    • “grants_read_access”: bei Benutzung des Constraints im Statement-Verbund (RefJOIN) wird Lesezugriff auf die Objekte der beteiligten Objekttypen gewährt, d.h. durch den RefJOIN werden evtl. bestehende Einschränkungen der Leserechte aufgehoben (falls true).
    • “is_deferred”: Constraint wird am Ende der Transaktion validiert (true), bzw. pro Statement validiert (false). Dadurch ist es möglich, die geforderte Bedingung durch verschiedene Statements innerhalb der Transaktion zu ignorieren, solange sie am Transaktionsende wieder erfüllt ist.
    • “is_join_default”: wird als Standard-Constraint bei JOIN-Anweisungen eingesetzt, falls das Schlüsselwort “default” angegeben wurde
    • “internal_name”: systeminterner Name des Constraints; kann als Bezugnahme zur Constraint-Angabe unter stacked_diag_params im Exception-Objekt verwendet werden.
    • “description”: Beschreibung der Bedingung, die durch den Constraint garantiert bzw. erzwungen wird
    • “errors”: JSON-Array der JSON-Dicts der zugeordneten Fehler
      • “code”: Fehlernummer
      • “type”: Fehlertypnummer
      • “description”: Fehlerbeschreibung
      • “details”: Fehler-Details
  • “attributes”: JSON-Dict, dessen Schlüssel die Attribute des Objekttyps sind

    • “data_type”: Datentyp-Name auf API-Ebene. Eine vollständige Übersicht aller Datentyp-Attribute ist im Objekttyp cntl.data_type zu finden.
    • “description_detail”: detaillierter Beschreibungstext des Attributes
    • “description_obj_type_scope”: kurze Bezeichnung des Attributes, die sich auf eine objekttyp-weite Sichtweise bezieht
    • “description_sys_scope”: kurze Bezeichnung des Attributes, die sich auf eine system-weite Sichtweise bezieht
    • “is_core”: eigenes, primär zugehöriges Attribut (Kern-Attribut) des Objekttyps (true). Es können auch Attribute anderer Objekttypen oder weitere Daten angegeben werden (false).
    • “is_deprecated”: Attribut wird in neueren Versionen nicht mehr unterstützt (true) bzw. weiterhin unterstützt (false).
    • “is_nullable”: Das Attribut darf den Wert null haben (true) bzw. nicht haben (false).
    • “supported_values”: erlaubte Werte für dieses Attribut. Nur vorhanden, wenn zutreffend. Unabhängig vom tatsächlichen Datentyp muss die Darstellung des <value> als Text erfolgen, da JSON-Keys nur als Text dargestellt werden können.
  • “referencing”: JSON-Dict, dessen Schlüssel die referenzierenden Constraint-Namen (child-seitig) sind
    • “attributes”: JSON-Array der Attribute des bezeichneten Constraints
    • “is_deferred”: Constraint wird am Ende der Transaktion validiert (true), bzw. pro Statement validiert (false)
    • “is_join_default”: wird als Standard-Constraint bei JOIN-Anweisungen eingesetzt, falls das Schlüsselwort “default” angegeben wurde
    • “on_delete”: Aktion, die beim Löschen des referenzierten (parent-seitigen) Datensatzes ausgeführt wird. Mögliche Werte:
      • “raise”: Exception werfen
      • “cascade”: kaskadiert löschen (in Folge werden auch alle referenzierenden Datensätze gelöscht)
      • “set null”: Fremdschlüssel aller referenzierenden Datensätze wird null gesetzt
      • “set default”: Fremdschlüssel aller referenzierenden Datensätze wird auf den internen Standardwert gesetzt
    • “references”: JSON-Dict des referenzierten Constraints (parent-seitig) mit den Schlüsseln
      • “name”: Constraint-Name
      • “object_type”: Objekttyp-Name des Constraints
      • “system”: System-Name des Objekttyps des Constraints
  • “referenceable”: JSON-Dict, dessen Schlüssel die referenzierbaren Constraint-Namen sind (s. Constraint-Typ “U”, “UP” oder “P”)
    • “attributes”: JSON-Array der Attribute des bezeichneten Constraints. Über die Werte dieser Attribute kann ein Objekt dieses Objekttyps eindeutig identifiziert werden.
    • “is_deferred”: Constraint wird am Ende der Transaktion validiert (true), bzw. pro Statement validiert (false)
    • “referenced_by”: Liste der JSON-Dicts aller referenzierenden Constraints. Umgekehrte Darstellung zu “referencing”, aber aus Sicht des referenzierten Objekttyps (parent). Ein Dict hat die Schlüssel
      • “name”: Constraint-Name
      • “object_type”: Objekttyp-Name des Constraints
      • “system”: System-Name des Objekttyps des Constraints
      • “is_join_default”: wird als Standard-Constraint bei JOIN-Anweisungen eingesetzt, falls das Schlüsselwort “default” angegeben wurde
    • “type”: Constraint-Typ. Wertebereich: wie oben “type” unter “constraints”.

Beispiel

Allgemeine Funktions-Datenstruktur

Parameter bzw. Objekttyp-Attribute, die nicht-ausführbaren Funktionen zugeordnet sind, sind nur im Objekttypindex, aber nicht im Funktionsindex sichtbar. Jede ausführbare Funktion eines Objekttyps wird nach folgendem Schema dargestellt:


{
  "fq_name": "<system_name>.<object_type_name>.<function_name>",
  "is_data_manipulating": true | false,
  "is_executable": true | false,
  "is_returning": true | false,
  "name": "<function_name>",
  "object_type": "<object_type_name>",
  "parameters": {
    "<parameter_name>": {
      "data_type": "<api_datatype_name>",
      "description_detail": "detaillierter Beschreibungstext des Parameters",
      "description_obj_type_scope": "objekttyp-weite Beschreibung des Parameters",
      "description_sys_scope": "system-weite Beschreibung des Parameters",
      "is_deprecated": true | false,
      "new": {
        "data_default": <default_value>,
        "is_nullable": true | false,
        "is_required": true | false
      },
      "old": {
        "data_default": <default_value>,
        "is_nullable": true | false,
        "is_required": true | false
      },
      "supported_values": {
        "<value>": "<value_description>"
      }
    }
  },
  "system": "<system_name>"
}

Bedeutung der Platzhalter (Variablen)

  • <system_name>: System-Name
  • <object_type_name>: Objekttyp-Name
  • <function_name>: Funktions-Name
  • <parameter_name>: Parameter-Name (bei Gleichnamigkeit zu Objekttyp-Attributnamen sind beide identisch)
  • <api_datatype_name>: Datentyp-Name des Attributs (Name auf API-Ebene)
  • <json_datatype_name>: Datentyp-Name des Attributs (Name auf JSON-Ebene), Wertebereich:
    • object
    • array
    • string
    • number
    • boolean
    • json (allgemein; kann ein beliebiger der vorgenannten JSON-Datentypen sein)
  • <value>: erlaubter Parameterwert
  • <value_description>: Erläuterung zu diesem Wert
  • <default_value>: Standardwert. Wird automatisch eingesetzt, wenn nicht null und wenn der Parameter in der jeweiligen Belegung nicht angegeben wurde.

Bedeutung der Bezeichner (Konstanten)

  • “system”: Name des Systems, dem der Objekttyp der Funktion zugeordnet ist
  • “object_type”: Name des Objekttyps der Funktion
  • “fq_name”: voll qualifizierter (system- und objekttyp-qualifizierter) Funktionsname
  • “is_data_manipulating”: modifizierende/datenschreibende Funktion (true) oder nur-datenlesende Funktion (false)
  • “is_executable”: Funktion ist ausführbar und kann damit in einer Transaktion angewendet werden
  • “is_returning”: Funktion ist rückgabefähig, d.h. sie kann eine nicht-leere Antwort zurückgeben
  • “name”: Funktionsname innerhalb eines Systems und Objekttyps
  • “parameters”: JSON-Dict, dessen Schlüssel die Funktionsparameter sind. Bei Gleichnamigkeit zu einem Attribut des Objekttyps entspricht der Parameter diesem Attribut.
    • “data_type”: Datentyp-Name auf API-Ebene. Eine vollständige Übersicht aller Datentyp-Attribute ist im Objekttyp cntl.data_type zu finden.
    • “description_detail”: detaillierter Beschreibungstext des Parameters
    • “description_obj_type_scope”: kurze Bezeichnung des Parameters, die sich auf eine objekttyp-weite Sichtweise bezieht
    • “description_sys_scope”: kurze Bezeichnung des Parameters, die sich auf eine system-weite Sichtweise bezieht
    • “is_deprecated”: Parameter wird in neueren Versionen nicht mehr unterstützt (true) bzw. weiterhin unterstützt (false).
    • “old”: Wertebelegung für den Zustand vor der Datenmodifikation (Altbelegung). Nur vorhanden, wenn zutreffend.
      • “data_default”: Standardwert, der in dieser Belegung eingesetzt wird, falls der Parameter nicht angegeben wurde. Erscheint nur, wenn der Standardwert nicht null ist.
      • “is_nullable”: Der Parameter darf in dieser Belegung den Wert null haben (true) bzw. nicht haben (false).
      • “is_required”: Der Parameter muss in dieser Belegung verwendet werden (true) bzw. kann weggelassen werden (false)
    • “new”: Wertebelegung für den Zustand nach der Datenmodifikation (Neubelegung). Nur vorhanden, wenn zutreffend.
      • “data_default”: Standardwert, der in dieser Belegung eingesetzt wird, falls der Parameter nicht angegeben wurde Erscheint nur, wenn der Standardwert nicht null ist.
      • “is_nullable”: Der Parameter darf in dieser Belegung den Wert null haben (true) bzw. nicht haben (false).
      • “is_required”: Der Parameter muss in dieser Belegung verwendet werden (true) bzw. kann weggelassen werden (false)
    • “supported_values”: erlaubte Werte für diesen Parameter. Nur vorhanden, wenn zutreffend. Unabhängig vom tatsächlichen Datentyp muss die Darstellung des <value> als Text erfolgen, da JSON-Keys nur als Text dargestellt werden können.

Je nach Funktion ist die Wertebelegung für old und/oder new definiert; mindestens aber eine davon. Die jeweils nicht definierte Wertebelegung wird weggelassen. Für Nur-Lesefunktionen ist immer die Altbelegung definiert.

Beispiel

Versionsangaben

Die explizite Versionsangabe aller Zugriffe ermöglicht es, neue Features schnell einzuführen, ohne Clients, die eine bestehende Version benutzen, auszusperren oder auf deren Versionsanpassung zu warten. Die Hauptnummer wird bei umfangreichen API-Änderungen erhöht, die Anpassungen bei den meisten Clients erfordern würden; die Unternummer bei solchen, die die meisten Clients nicht betrifft.

Beide Versionsangaben (<vmajor>, <vminor>) sind numerisch (natürliche Zahl). Um den Status einer Version zu kennzeichnen, gibt es neben der numerischen Versionsangabe (Standard-Angabe) auch eine semantische Angabe. Sie bezieht sich auf die Version der internen Schnittstelle zwischen WebAPI und der darunterliegenden Datenbank (NetDB). Die Versionsangabe bei Requests kann nur numerisch sein. Beispiele für den Status einer Version (semantische Versionsangaben):

  • alpha: Entwicklung
  • beta: Testbetrieb für begrenzten Anwenderkreis freigegeben
  • rc: Veröffentlichungskandidat (release candidate), abschließende Testversion
  • release: aktuelle Version
  • oldrelease: veraltete Version (Vorgänger der aktuellen Version)
  • deprecated: nicht mehr unterstützte Version (Vorgänger der veralteten Version)

Beispiel-Szenarium für einen Versionswechsel

  • vorher:
    • semantische Version beta ist numerischer Version 1.1 zugeordnet
    • semantische Version release ist numerischer Version 1.0 zugeordnet
    • semantische Version oldrelease ist numerischer Version 0.9 zugeordnet
    • semantische Version deprecated ist numerischer Version 0.8 zugeordnet
  • nachher (Freigabe der beta-Version als neue release-Version)
    • [NEU] semantische Version beta ist numerischer Version 1.2 zugeordnet
    • semantische Version release ist numerischer Version 1.1 zugeordnet
    • semantische Version oldrelease ist numerischer Version 1.0 zugeordnet
    • semantische Version deprecated ist numerischer Version 0.9 zugeordnet
    • [ALT] numerische Version 0.8 ist abgeschaltet und nicht mehr verfügbar