13 KiB
Plan: PCK_NET_STORAGE
Stand: 2026-04-08 Status: Implementiert
Ziel
PL/SQL Package PCK_NET_STORAGE kapselt alle Zugriffe auf OCI Object Storage über REST API.
Wird in Oracle APEX für einen Dateimanager genutzt (Browse, Upload, Download, Rename, Move, Delete).
Dateien
tables/
lg_app_log.tab
types/
t_net_storage_row.typ ← Schema-Level Type (siehe Hinweis)
t_net_storage_tab.typ ← Nested Table von t_net_storage_row
packages/
pck_log.pkh / pck_log.pkb
pck_net_storage.pkh / pck_net_storage.pkb
pck_auto_import.pkh / pck_auto_import.pkb
Hinweis Schema-Level Types: TABLE() in SQL erfordert in Oracle schema-level Types.
t_net_storage_row / t_net_storage_tab werden ausschließlich intern für den
sys_refcursor von f_list_objects benötigt — keine Auswirkung auf die öffentliche API.
Authentifizierung
APEX Web Credentials (Typ: Oracle Cloud Infrastructure)
Static ID: OCI_OBJ_STORE_CRED
Wird in APEX Admin UI einmalig angelegt mit: OCI User OCID, Tenancy OCID, Fingerprint, Private Key (PEM).
In PL/SQL referenziert via p_credential_static_id => c_credential_id in apex_web_service.make_rest_request.
Grund: DBMS_CLOUD ist auf dieser DB nicht installiert. APEX übernimmt OCI HTTP Signature V1 (SHA-256) automatisch.
Konfigurationsparameter
Alle zur Laufzeit via pck_system.f_get_par_wert_by_programmid:
| Parameter-ID | Inhalt |
|---|---|
NETSTORE_BUCKET |
OCI Object Storage Bucket-Name, z.B. INKA_NET_STORAGE |
NETSTORE_NAMESPACE |
OCI Object Storage Namespace, z.B. frhqaxi5sgcg |
NETSTORE_REGION |
OCI Region unter der der Object Storage erreichbar ist, z.B. eu-frankfurt-1. |
NETSTORE_WALLET_PATH |
Der Pfad zur Wallet-Datei auf dem DB Server, die für die Zertifkatsvalidierung bei Verbindungen zum OCI Object Storage genutzt werden muss. z.B.: file:/u01/app/oracle/product/19.0.0.0/dbhome_1/wallets/digicert-global-root-g2-wallet |
NETSTORE_TENANT_ID |
Erlaubter Root-Prefix, z.B. mandant_42/ |
NETSTORE_CRED_ID |
Credential Static ID für apex_web_service calls, z.B. 'OCI_INKA_NET_STORE' |
NETSTORE_MARKER_DB |
Name der Marker-Datei im Object Store, der von Automaton abgelegt wird, um zu signalisieren, dass der entsprechende Unterordner komplett hochgeladen wurde. Verhindert die Verarbeitung von unvollständig hochgeladenen Ordnern. z.B.: _READY_FOR_DB_PROCESSING_ |
NETSTORE_MARKER_SB |
Name der Marker-Datei im Object Store, der von DB beim Verarbeiten angelegt wird, wenn eine oder mehrere Dateien eines ZIPs nicht automatisiert importiert werden konnten. Das soll signalisieren, dass ein Sachbearbeiter die Dateien in diesem Unterordner manuell prüfen und importieren muss. z.B.: _BITTE_PRÜFEN_ |
NETSTORE_BA_PREFIX |
Pfad in Object Storage, wo BA-Daten liegen. Muss mit einem / enden, z.B. BA/Eingang/ |
NETSTORE_BA_IMPORT |
Name des Unterordners von NETSTORE_BA_PREFIX im Object Storage, wo entpackte Dateien, die noch importiert werden müssen, zwischengespeichert werden. |
NETSTORE_BA_ARCHIV |
Der Basis-Name des Unterordners von NETSTORE_BA_PREFIX im Object Storage, wo verarbeitete BA-Imports abgelegt werden. Der Name darf nicht mit / enden oder beginnen, z.B. "Verarbeitet". Beim Import wird an diesen Namen immer die aktuelle Jahreszahl angehangen, sodass der finale Ordner z.B. "Verarbeitet 2026" heißt. |
BA_IMPORT_SB_MIT_ID |
Mitarbeiter-ID für Import von BA Daten (z.B. Korrespondenzen). Diese Mitarbeiter-ID bekommt eine Wiedervorlage, für jede Datei, die nicht automatisch importiert werden konnte. |
AUTOMATON_BASE_URL |
Base-URL des Quarkus Dateieingang Service, z.B. http://dateieingang:8080 |
AUTOMATON_API_KEY |
API-Key für den Quarkus Dateieingang Service (Header X-Api-Key) |
NETSTORE_ORDS_APIKEY |
API-Key, den der Quarkus Server nutzt, um den ORDS aufzurufen |
Öffentliche Schnittstelle (Package Spec)
Record Type t_object_meta
| Feld | Typ |
|---|---|
object_name |
varchar2(1024) |
object_size |
number |
last_modified |
date |
content_type |
varchar2(256) |
etag |
varchar2(256) |
Öffentliche Prozeduren und Funktionen
Namenskonvention: Prozeduren p_-Präfix, Funktionen f_-Präfix. Parameter immer i_-Präfix.
| Name | Typ | Signatur | Rückgabe | Anmerkung |
|---|---|---|---|---|
f_list_objects |
Funktion | (i_prefix, i_delimiter, i_start_with, i_limit) |
sys_refcursor |
Cursor-Spalten: object_name, object_size, last_modified, is_folder(Y/N), etag |
f_download_object |
Funktion | (i_object_key) |
blob |
|
p_upload_object |
Prozedur | (i_object_key, i_content blob, i_content_type) |
— | |
p_delete_object |
Prozedur | (i_object_key) |
— | |
p_delete_folder |
Prozedur | (i_prefix) |
— | intern: f_list_objects-Loop + p_delete_object |
p_rename_object |
Prozedur | (i_object_key, i_new_name) |
— | intern: OCI RenameObject API |
p_move_object |
Prozedur | (i_object_key, i_target_prefix) |
— | intern: OCI RenameObject API |
p_create_folder |
Prozedur | (i_prefix, i_folder_name) |
— | PUT leeres Objekt mit trailing / |
f_get_object_metadata |
Funktion | (i_object_key) |
t_object_meta |
HEAD Request |
Rechteprüfungen:
Zu beginn von delete-operationen muss folgender Aufruf durchgeführt werden, um die Rechte des nutzers zu prüfen: pck_mitarbeiterrecht.p_hat_recht('ADMIN');
Bei anderen schreib-Operationen, wie rename oder move muss die Prüfung von folgendem Recht erfolgen:
pck_mitarbeiterrecht.p_hat_recht('SCHREIBEN_ALLES');
Bei lese-Operationen wie f_get_object_metadata und f_download_object muss folgendes Recht geprüft werden:
pck_mitarbeiterrecht.p_hat_recht('LESEN_ALLES');
Schlägt eine Prüfung fehl, wirft die p_hat_recht Prozedur einen speziellen Application Error mit Fehlermeldung für den Benutzer.
Private Helpers (nur Body)
| Name | Typ | Zweck |
|---|---|---|
f_get_cfg |
Funktion | Wrapper für pck_system.f_get_par_wert_by_programmid |
f_build_url |
Funktion | Baut vollständige OCI REST URL (/n/{ns}/b/{bucket}/o/{key}) |
p_assert_allowed |
Prozedur | Tenant-Prefix-Guard + Path-Traversal-Prüfung |
f_make_request |
Funktion | Wrapper für apex_web_service.make_rest_request, wertet HTTP-Status aus, gibt Response-Body zurück |
Pagination (f_list_objects)
OCI liefert max. 1000 Objekte pro Request. f_list_objects ruft OCI intern in einer Schleife auf
(nextStartWith-Token aus JSON-Response) bis alle Seiten geladen sind. Ergebnis wird in einer
lokalen Collection gesammelt, am Ende als sys_refcursor geöffnet.
i_limit = 0 → unbegrenzt, i_limit > 0 → Abbruch wenn Limit erreicht.
OCI-Response trennt Unterordner (prefixes-Array) von Dateien (objects-Array) — beide werden
eingesammelt, Ordner mit is_folder = 'Y'.
Fehlerbehandlung
f_make_request wirft bei HTTP-Fehler:
| HTTP-Status | Fehlercode | Meldung |
|---|---|---|
| 404 | -20001 | Object not found: {key} |
| 401 / 403 | -20002 | OCI authentication failed |
| 409 | -20007 | Object already exists: {key} |
| sonstige 4xx/5xx | -20006 | OCI API error {status}: {response body} |
p_assert_allowed wirft:
| Fall | Fehlercode | Meldung |
|---|---|---|
.. im Pfad |
-20004 | Path traversal attempt detected |
| Pfad außerhalb Tenant-Prefix | -20005 | Access denied: outside tenant scope |
Bucket nicht erreichbar → -20003 via f_make_request
Sicherheit / Tenant-Isolation
p_assert_allowed wird am Anfang jeder öffentlichen Funktion/Prozedur aufgerufen:
- Tenant-Prefix laden via
f_get_cfg('NETSTORE_TENANT_ID') ..-Sequenzen im übergebenen Key prüfen → -20004- Prüfen ob Key mit Tenant-Prefix beginnt → -20005
Dateieingang-Verarbeitung
OCI-Struktur
Der Quarkus Automaton legt Dateien in einem Unterordner ab, der nach der ZIP-Datei benannt ist. Unterordner innerhalb der ZIP werden beibehalten:
eingang/<zip-name>/datei1.csv
eingang/<zip-name>/unterordner/datei3.csv
eingang/<zip-name>/_READY_FOR_DB_PROCESSING_ ← Marker
Marker-Datei
Der Quarkus Automaton legt nach erfolgreichem Upload aller Dateien eine leere Marker-Datei ab:
eingang/<zip-name>/_READY_FOR_DB_PROCESSING_
Die DB verarbeitet einen Unterordner ausschließlich wenn der Marker vorhanden ist. Der Marker wird erst gelöscht wenn alle Dateien des Unterordners erfolgreich verarbeitet wurden — so werden fehlgeschlagene Dateien beim nächsten Lauf erneut versucht.
Prozedur p_run_ba_korrespondenz_dateieingang_automation (in pck_auto_import) — APEX Automation Einstiegspunkt
Von der APEX Automation (stündlich) aufgerufen. Orchestriert den Gesamtablauf:
1. p_process_incoming_ba_data aufrufen
→ verarbeitet Batches die bereits in OCI liegen (Fallback, falls ORDS-Aufruf letzten Lauf fehlschlug)
→ Fehler eskalieren (APEX Automation markiert Lauf als fehlerhaft)
2. HTTP POST an AUTOMATON_BASE_URL/api/process-incoming (Header: X-Api-Key: AUTOMATON_API_KEY)
→ 202 Accepted: Pipeline gestartet
→ 409 Conflict: Service läuft bereits — kein zweiter Lauf, kein Fehler
→ sonstige Fehler: loggen, nicht eskalieren (nächster Lauf führt Schritt 1 wieder aus)
Prozedur p_process_incoming_ba_data (in pck_auto_import) — ORDS-Einstiegspunkt
1. Unterordner von eingang/ auflisten (f_list_objects mit delimiter='/')
2. Für jeden Unterordner:
a. Marker eingang/<zip-name>/_READY_FOR_DB_PROCESSING_ vorhanden?
→ nein: überspringen (unvollständiger Upload)
b. Alle Dateien in eingang/<zip-name>/ auflisten (ohne Marker)
c. Für jede Datei einzeln:
begin
Fachliche Verarbeitung (Import) — noch kein Commit
log_object_ref := 'eingang/<zip-name>/datei.csv' ← ZIP aus Pfad ableitbar
pck_net_storage.p_move_object(datei → Zielordner/<zip-name>/)
commit
pck_log.p_info(...)
exception when others then
rollback
pck_log.p_error(i_object_ref => 'eingang/<zip-name>/datei.csv', ...)
end
d. Noch Dateien in eingang/<zip-name>/ (außer Marker)?
→ nein: pck_net_storage.p_delete_object(Marker) — Batch abgeschlossen
→ ja: Marker bleibt — nächster Lauf versucht verbleibende Dateien
Rollback nach fehlgeschlagenem p_move_object macht auch den Import rückgängig.
Kein Verhalten wird aus lg_app_log abgeleitet — es dient ausschließlich dem Audit.
ORDS-Endpunkt
POST /ords/.../net_storage/process_incoming_ba_data → ruft p_process_incoming_ba_data auf.
Wird von automaton nach erfolgreichem Upload aufgerufen.
Schlägt der Aufruf fehl: APEX Automation greift beim nächsten Stundenlauf.
APEX REST Data Source (manuell einrichten)
Für das APEX Interactive Report im Dateimanager-Browser:
Name: NET_STORAGE_LIST_OBJECTS
Base-URL: https://objectstorage.{region}.oraclecloud.com
Endpoint: /n/{namespace}/b/{bucket}/o
Method: GET
Web Credential: OCI_OBJ_STORE_CRED
URL-Parameter:
prefix → aktueller Ordner-Prefix (Bind-Variable aus APEX)
delimiter → /
limit → 1000
start → für Pagination (optional)
Spalten-Mapping:
objects[*].name → OBJECT_NAME
objects[*].size → OBJECT_SIZE
objects[*].timeModified → LAST_MODIFIED
objects[*].etag → ETAG
Hinweis: prefixes (Unterordner) und objects (Dateien) sind zwei separate JSON-Arrays.
Für ein gemeinsames IR entweder zwei REST Sources mit UNION, oder f_list_objects über
eine APEX Collection als IR-Quelle nutzen.
Logging: lg_app_log + pck_log
Tabelle lg_app_log
Allgemeine Anwendungs-Log-Tabelle, modulübergreifend nutzbar. Keine Indizes (Log-Volumen ist gering, Ad-hoc-Suche reicht ohne Index).
| Spalte | Typ | Pflicht | Beschreibung |
|---|---|---|---|
lg_app_log_id |
number generated by default as identity |
ja | PK |
log_timestamp |
timestamp |
ja | Zeitpunkt des Log-Eintrags |
log_level |
varchar2(10 char) |
ja | INFO, WARN, ERROR |
log_module |
varchar2(100 char) |
ja | z.B. PCK_NET_STORAGE |
log_action |
varchar2(100 char) |
nein | z.B. PROCESS_FILE |
log_object_ref |
varchar2(512 char) |
nein | z.B. eingang/datei.csv |
log_message |
varchar2(4000 char) |
nein | Kurzmeldung |
log_detail |
clob |
nein | Stack Trace, JSON, etc. |
log_user |
varchar2(100 char) |
nein | APEX- oder DB-User |
log_session_id |
number |
nein | Oracle Session-ID |
Keine Audit-Spalten (Log-Tabelle ist self-auditing durch log_timestamp + log_user).
Package pck_log
Öffentliche Prozeduren:
| Name | Signatur |
|---|---|
p_info |
(i_module, i_action, i_message, i_object_ref default null) |
p_warn |
(i_module, i_action, i_message, i_object_ref default null) |
p_error |
(i_module, i_action, i_message, i_detail default null, i_object_ref default null) |
Intern immer PRAGMA AUTONOMOUS_TRANSACTION + COMMIT — Log-Einträge sind
transaktionsunabhängig und überleben Rollbacks der Haupttransaktion.
OCI API Endpunkte (Referenz)
| Operation | Method | Pfad |
|---|---|---|
| List Objects | GET | /n/{ns}/b/{bucket}/o?prefix=...&delimiter=/ |
| Get Object | GET | /n/{ns}/b/{bucket}/o/{objectName} |
| Put Object | PUT | /n/{ns}/b/{bucket}/o/{objectName} |
| Delete Object | DELETE | /n/{ns}/b/{bucket}/o/{objectName} |
| Head Object | HEAD | /n/{ns}/b/{bucket}/o/{objectName} |
| Rename Object | POST | /n/{ns}/b/{bucket}/actions/renameObject |