From 27f9a397b2bb45058c274cc9be27df64d4494b97 Mon Sep 17 00:00:00 2001 From: Sudo-JHare Date: Tue, 12 Aug 2025 23:10:52 +1000 Subject: [PATCH] update --- app.py | 17 +++--- templates/retrieve_split_data.html | 85 ++++++++++++++---------------- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/app.py b/app.py index 371c942..5347cb8 100644 --- a/app.py +++ b/app.py @@ -1907,23 +1907,22 @@ def proxy_hapi(subpath): return jsonify({'resourceType': 'OperationOutcome', 'issue': [{'severity': 'error', 'code': 'exception', 'diagnostics': 'An unexpected error occurred within the FHIR proxy.', 'details': {'text': str(e)}}]}), 500 -@app.route('/api/stream-retrieve', methods=['GET']) +@app.route('/api/stream-retrieve', methods=['POST']) def stream_retrieve_bundles(): """ Handles streaming FHIR bundle retrieval. This endpoint directly calls the service function, bypassing the proxy to avoid conflicts. It receives the target URL, - resources, and other parameters from the front-end via URL query parameters. + resources, and other parameters from the front-end via a JSON body. """ def generate_stream(): - # Push the application context manually for the generator's lifetime with app.app_context(): - # Extract parameters from query string - fhir_server_url = request.args.get('fhir_server_url') - resources = request.args.getlist('resources') - validate_references = request.args.get('validate_references', 'false').lower() == 'true' - fetch_reference_bundles = request.args.get('fetch_reference_bundles', 'false').lower() == 'true' + # Extract parameters from JSON body + data = request.json + fhir_server_url = data.get('fhir_server_url') + resources = data.get('resources') + validate_references = data.get('validate_references', False) + fetch_reference_bundles = data.get('fetch_reference_bundles', False) - # Extract authentication headers auth_token = request.headers.get('Authorization') auth_type = 'bearer' if auth_token and auth_token.lower().startswith('bearer') else 'basic' if auth_token and auth_token.lower().startswith('basic') else 'none' diff --git a/templates/retrieve_split_data.html b/templates/retrieve_split_data.html index b47b925..9ebc229 100644 --- a/templates/retrieve_split_data.html +++ b/templates/retrieve_split_data.html @@ -301,10 +301,10 @@ document.addEventListener('DOMContentLoaded', () => { fetchMetadataButton.textContent = 'Fetching...'; try { - const fetchUrl = '/fhir/metadata'; + // Updated metadata fetch logic to handle both local and custom URLs + const fetchUrl = useLocalHapi ? '/fhir/metadata' : `${customUrl}/metadata`; const headers = { 'Accept': 'application/fhir+json' }; - if (!useLocalHapi && customUrl) { - headers['X-Target-FHIR-Server'] = customUrl; + if (!useLocalHapi) { if (authTypeSelect && authTypeSelect.value !== 'none') { const authType = authTypeSelect.value; if (authType === 'bearer' && bearerTokenInput && bearerTokenInput.value) { @@ -314,12 +314,10 @@ document.addEventListener('DOMContentLoaded', () => { headers['Authorization'] = `Basic ${credentials}`; } } - console.log(`Fetching metadata via proxy with X-Target-FHIR-Server: ${customUrl}`); + console.log(`Fetching metadata directly from: ${customUrl}`); } else { - console.log("Fetching metadata via proxy for local HAPI server"); + console.log("Fetching metadata from local HAPI server via proxy"); } - console.log(`Proxy Fetch URL: ${fetchUrl}`); - console.log(`Request Headers sent TO PROXY: ${JSON.stringify(headers)}`); const response = await fetch(fetchUrl, { method: 'GET', headers: headers }); @@ -383,17 +381,8 @@ document.addEventListener('DOMContentLoaded', () => { if (icon) icon.style.display = 'inline-block'; return; } - + const currentFhirServerUrl = useLocalHapi ? null : fhirServerUrlInput.value.trim().replace(/\/+$/, ''); - if (!useLocalHapi && !currentFhirServerUrl) { - alert('Custom FHIR Server URL is required.'); - fhirServerUrlInput.classList.add('is-invalid'); - retrieveButton.disabled = false; - if (spinner) spinner.style.display = 'none'; - if (icon) icon.style.display = 'inline-block'; - return; - } - const authType = authTypeSelect?.value; const authHeader = (authType === 'bearer' && bearerTokenInput?.value) ? `Bearer ${bearerTokenInput.value}` : (authType === 'basic' && usernameInput?.value && passwordInput?.value) ? `Basic ${btoa(`${usernameInput.value}:${passwordInput.value}`)}` @@ -402,16 +391,19 @@ document.addEventListener('DOMContentLoaded', () => { const validateReferences = validateReferencesCheckbox?.checked ? 'true' : 'false'; const fetchReferenceBundles = validateReferences === 'true' && fetchReferenceBundlesCheckbox?.checked ? 'true' : 'false'; - // --- Stream the logs directly from the server --- + // --- Stream the logs directly from the new backend endpoint --- const url = new URL('/api/stream-retrieve', window.location.origin); - if (currentFhirServerUrl) { - url.searchParams.set('proxy-target', currentFhirServerUrl); - } - selectedResources.forEach(res => url.searchParams.append('resources', res)); - url.searchParams.set('validate_references', validateReferences); - url.searchParams.set('fetch_reference_bundles', fetchReferenceBundles); - + + // Use POST for the new endpoint + const formData = { + fhir_server_url: currentFhirServerUrl, + resources: selectedResources, + validate_references: validateReferences, + fetch_reference_bundles: fetchReferenceBundles + }; + const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/x-ndjson', }; if (authHeader) { @@ -425,10 +417,10 @@ document.addEventListener('DOMContentLoaded', () => { headers['X-API-Key'] = apiKey; } - console.log(`Submitting retrieve request. URL: ${url.toString()}, Headers: ${JSON.stringify(headers)}`); + console.log(`Submitting retrieve request. URL: ${url.toString()}, Headers: ${JSON.stringify(headers)}, Body: ${JSON.stringify(formData)}`); try { - const response = await fetch(url.toString(), { method: 'GET', headers: headers }); + const response = await fetch(url.toString(), { method: 'POST', headers: headers, body: JSON.stringify(formData) }); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: 'Failed to parse error response.' })); @@ -647,23 +639,24 @@ document.addEventListener('DOMContentLoaded', () => { link.download = 'split_resources.zip'; document.body.appendChild(link); link.click(); -document.body.removeChild(link); -setTimeout(() => { -const csrfToken = splitForm.querySelector('input[name="csrf_token"]')?.value || ''; -fetch('/clear-session', { method: 'POST', headers: { 'X-CSRFToken': csrfToken }}) -.then(() => console.log("Session clear requested after split download.")) -.catch(err => console.error("Session clear failed:", err)); -downloadSplitButton.style.display = 'none'; -splitZipPath = null; -}, 500); -} else { -console.error("No split ZIP path available for download"); -alert("Download error: No file path available."); -} + document.body.removeChild(link); + setTimeout(() => { + const csrfToken = splitForm.querySelector('input[name="csrf_token"]')?.value || ''; + fetch('/clear-session', { method: 'POST', headers: { 'X-CSRFToken': csrfToken }}) + .then(() => console.log("Session clear requested after split download.")) + .catch(err => console.error("Session clear failed:", err)); + downloadSplitButton.style.display = 'none'; + splitZipPath = null; + }, 500); + } else { + console.error("No split ZIP path available for download"); + alert("Download error: No file path available."); + } + }); + + // --- Initial Setup Calls --- + updateBundleSourceUI(); + updateServerToggleUI(); + toggleFetchReferenceBundles(); }); -updateBundleSourceUI(); -updateServerToggleUI(); -toggleFetchReferenceBundles(); -}); - -{% endblock %} \ No newline at end of file + \ No newline at end of file