-- ============================================================================= -- DC_BACKEND_PKG -- PL/SQL-Schnittstelle zum Quarkus dc-backend (Kubernetes Service dc-backend:8090) -- -- Voraussetzungen: -- - APEX_WEB_SERVICE-Berechtigung für das Schema: -- GRANT EXECUTE ON APEX_240100.APEX_WEB_SERVICE TO ; -- APEX-Version anpassen -- - ACL für ausgehende HTTP-Verbindung im selben Namespace: -- DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE( -- host => 'dc-backend', lower_port => 8090, upper_port => 8090, -- ace => xs$ace_type(privilege_list => xs$name_list('connect','resolve'), -- principal_name => '', principal_type => xs_acl.ptype_db)); -- -- Deployment: -- sqlplus user/pass@db @"Scripts/DC_BACKEND_PKG.sql" -- ============================================================================= -- ============================================================================= -- Package Spec -- ============================================================================= CREATE OR REPLACE PACKAGE DC_BACKEND_PKG AS -- Basis-URL des dc-backend Kubernetes Service c_base_url CONSTANT VARCHAR2(200) := 'http://dc-backend:8090'; -- API-Key (X-API-KEY Header). -- Leer = kein Auth (Dev-Modus). -- Zur Laufzeit setzen: DC_BACKEND_PKG.g_api_key := 'mein-key'; g_api_key VARCHAR2(500) := ''; -- ------------------------------------------------------------------------- -- Startet die KI-gestützte Dokumentenprüfung für ein Projekt (asynchron). -- Das Projekt muss im Status PENDING sein. -- POST /check/{p_project_id} → 202 Accepted -- -- p_project_id ID des dc_projects-Eintrags -- p_status_code HTTP-Statuscode der Antwort (202 = OK, 409 = nicht PENDING) -- p_response JSON-Antwort des Backend -- ------------------------------------------------------------------------- PROCEDURE start_check( p_project_id IN NUMBER, p_status_code OUT NUMBER, p_response OUT VARCHAR2 ); -- ------------------------------------------------------------------------- -- Health-Check: prüft ob der dc-backend Service erreichbar ist. -- GET /check/health → 200 UP -- -- Rückgabe: TRUE = Service läuft, FALSE = nicht erreichbar -- ------------------------------------------------------------------------- FUNCTION health_check RETURN BOOLEAN; -- ------------------------------------------------------------------------- -- Startet die Prüfung und wirft eine Exception wenn nicht 202 zurückkommt. -- Convenience-Wrapper für direkte APEX-Button-Aufrufe. -- ------------------------------------------------------------------------- PROCEDURE start_check_or_raise( p_project_id IN NUMBER ); -- ------------------------------------------------------------------------- -- Fortschritts-Record -- ------------------------------------------------------------------------- TYPE t_project_status IS RECORD ( status dc_projects.status%TYPE, processing_progress dc_projects.progress%TYPE, is_completed BOOLEAN, is_running BOOLEAN, is_pending BOOLEAN ); -- ------------------------------------------------------------------------- -- Liest Status und Fortschritt eines Projekts direkt aus der DB. -- Kein REST-Aufruf – sofort und transaktionssicher. -- -- Beispiel: -- l_s := DC_BACKEND_PKG.get_status(1); -- DBMS_OUTPUT.PUT_LINE(l_s.status || ' ' || l_s.processing_progress || '%'); -- ------------------------------------------------------------------------- FUNCTION get_status( p_project_id IN NUMBER ) RETURN t_project_status; -- ------------------------------------------------------------------------- -- Wartet (polling) bis das Projekt COMPLETED oder ein Timeout erreicht ist. -- Wirft eine Exception bei Timeout oder wenn das Projekt nicht gefunden wird. -- -- p_timeout_sec Max. Wartezeit in Sekunden (Default: 1800 = 30 Min.) -- p_interval_sec Polling-Intervall in Sekunden (Default: 5) -- ------------------------------------------------------------------------- PROCEDURE wait_for_completion( p_project_id IN NUMBER, p_timeout_sec IN NUMBER DEFAULT 1800, p_interval_sec IN NUMBER DEFAULT 5 ); END DC_BACKEND_PKG; / -- ============================================================================= -- Package Body -- ============================================================================= CREATE OR REPLACE PACKAGE BODY DC_BACKEND_PKG AS -- ------------------------------------------------------------------------- -- Setzt gemeinsame Request-Header (Content-Type + optional API-Key) -- ------------------------------------------------------------------------- PROCEDURE set_headers IS l_idx PLS_INTEGER := 1; BEGIN APEX_WEB_SERVICE.G_REQUEST_HEADERS.DELETE; APEX_WEB_SERVICE.G_REQUEST_HEADERS(l_idx).NAME := 'Content-Type'; APEX_WEB_SERVICE.G_REQUEST_HEADERS(l_idx).VALUE := 'application/json'; l_idx := l_idx + 1; IF g_api_key IS NOT NULL AND LENGTH(TRIM(g_api_key)) > 0 THEN APEX_WEB_SERVICE.G_REQUEST_HEADERS(l_idx).NAME := 'X-API-KEY'; APEX_WEB_SERVICE.G_REQUEST_HEADERS(l_idx).VALUE := g_api_key; END IF; END set_headers; -- ------------------------------------------------------------------------- PROCEDURE start_check( p_project_id IN NUMBER, p_status_code OUT NUMBER, p_response OUT VARCHAR2 ) IS l_url VARCHAR2(500); l_response CLOB; BEGIN l_url := c_base_url || '/check/' || TO_CHAR(p_project_id); set_headers(); l_response := APEX_WEB_SERVICE.MAKE_REST_REQUEST( p_url => l_url, p_http_method => 'POST', p_body => '{}' -- leerer Body, projectId ist im Pfad ); p_status_code := APEX_WEB_SERVICE.G_STATUS_CODE; p_response := SUBSTR(l_response, 1, 4000); EXCEPTION WHEN OTHERS THEN p_status_code := -1; p_response := 'Verbindungsfehler: ' || SQLERRM; END start_check; -- ------------------------------------------------------------------------- FUNCTION health_check RETURN BOOLEAN IS l_url VARCHAR2(500); l_response CLOB; l_status NUMBER; BEGIN l_url := c_base_url || '/check/health'; set_headers(); l_response := APEX_WEB_SERVICE.MAKE_REST_REQUEST( p_url => l_url, p_http_method => 'GET' ); l_status := APEX_WEB_SERVICE.G_STATUS_CODE; RETURN l_status = 200; EXCEPTION WHEN OTHERS THEN RETURN FALSE; END health_check; -- ------------------------------------------------------------------------- PROCEDURE start_check_or_raise( p_project_id IN NUMBER ) IS l_status NUMBER; l_response VARCHAR2(4000); BEGIN start_check( p_project_id => p_project_id, p_status_code => l_status, p_response => l_response ); IF l_status = 202 THEN -- OK: Verarbeitung wurde gestartet NULL; ELSIF l_status = 409 THEN RAISE_APPLICATION_ERROR(-20100, 'Projekt ' || p_project_id || ' ist nicht im Status PENDING.'); ELSIF l_status = 401 THEN RAISE_APPLICATION_ERROR(-20101, 'Ungültiger API-Key für dc-backend.'); ELSIF l_status = -1 THEN RAISE_APPLICATION_ERROR(-20102, 'dc-backend nicht erreichbar: ' || l_response); ELSE RAISE_APPLICATION_ERROR(-20103, 'Unerwarteter HTTP-Status ' || l_status || ': ' || l_response); END IF; END start_check_or_raise; -- ------------------------------------------------------------------------- FUNCTION get_status( p_project_id IN NUMBER ) RETURN t_project_status IS l_rec t_project_status; BEGIN SELECT status, progress INTO l_rec.status, l_rec.processing_progress FROM dc_projects WHERE id = p_project_id; l_rec.is_completed := l_rec.status = 'COMPLETED'; l_rec.is_running := l_rec.status = 'IN_PROGRESS'; l_rec.is_pending := l_rec.status = 'PENDING'; RETURN l_rec; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(-20110, 'Projekt ' || p_project_id || ' nicht gefunden.'); END get_status; -- ------------------------------------------------------------------------- PROCEDURE wait_for_completion( p_project_id IN NUMBER, p_timeout_sec IN NUMBER DEFAULT 1800, p_interval_sec IN NUMBER DEFAULT 5 ) IS l_start TIMESTAMP := SYSTIMESTAMP; l_status t_project_status; BEGIN LOOP l_status := get_status(p_project_id); EXIT WHEN l_status.is_completed; IF EXTRACT(SECOND FROM (SYSTIMESTAMP - l_start)) + EXTRACT(MINUTE FROM (SYSTIMESTAMP - l_start)) * 60 + EXTRACT(HOUR FROM (SYSTIMESTAMP - l_start)) * 3600 > p_timeout_sec THEN RAISE_APPLICATION_ERROR(-20111, 'Timeout nach ' || p_timeout_sec || 's – Projekt ' || p_project_id || ' noch im Status ' || l_status.status || ' (' || l_status.processing_progress || '%).'); END IF; DBMS_SESSION.SLEEP(p_interval_sec); END LOOP; END wait_for_completion; END DC_BACKEND_PKG; / -- ============================================================================= -- Schnelltest (optional, auskommentiert) -- ============================================================================= /* -- Health-Check BEGIN IF DC_BACKEND_PKG.health_check() THEN DBMS_OUTPUT.PUT_LINE('dc-backend: UP'); ELSE DBMS_OUTPUT.PUT_LINE('dc-backend: NICHT ERREICHBAR'); END IF; END; / -- Prüfung starten (Projekt-ID anpassen) DECLARE l_status NUMBER; l_response VARCHAR2(4000); BEGIN DC_BACKEND_PKG.start_check( p_project_id => 1, p_status_code => l_status, p_response => l_response ); DBMS_OUTPUT.PUT_LINE('Status: ' || l_status); DBMS_OUTPUT.PUT_LINE('Response: ' || l_response); END; / */