mirror of
https://github.com/Sudo-JHare/FHIRFLARE-IG-Toolkit.git
synced 2025-11-05 17:45:14 +00:00
Compare commits
6 Commits
5f4e1b7207
...
62ff8e8a28
| Author | SHA1 | Date | |
|---|---|---|---|
| 62ff8e8a28 | |||
| 91fdaa89f9 | |||
| 27f9a397b2 | |||
| a76180fd48 | |||
| ffeef91166 | |||
| 9cf99ec78d |
43
app.py
43
app.py
@ -1905,7 +1905,48 @@ def proxy_hapi(subpath):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected proxy error for {final_url}: {str(e)}", exc_info=True)
|
logger.error(f"Unexpected proxy error for {final_url}: {str(e)}", exc_info=True)
|
||||||
return jsonify({'resourceType': 'OperationOutcome', 'issue': [{'severity': 'error', 'code': 'exception', 'diagnostics': 'An unexpected error occurred within the FHIR proxy.', 'details': {'text': str(e)}}]}), 500
|
return jsonify({'resourceType': 'OperationOutcome', 'issue': [{'severity': 'error', 'code': 'exception', 'diagnostics': 'An unexpected error occurred within the FHIR proxy.', 'details': {'text': str(e)}}]}), 500
|
||||||
# --- End of corrected proxy_hapi function ---
|
|
||||||
|
|
||||||
|
@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 a JSON body.
|
||||||
|
"""
|
||||||
|
def generate_stream():
|
||||||
|
with app.app_context():
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
temp_dir = tempfile.gettempdir()
|
||||||
|
zip_filename = f"retrieved_bundles_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.zip"
|
||||||
|
output_zip = os.path.join(temp_dir, zip_filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
yield from services.retrieve_bundles(
|
||||||
|
fhir_server_url=fhir_server_url,
|
||||||
|
resources=resources,
|
||||||
|
output_zip=output_zip,
|
||||||
|
validate_references=validate_references,
|
||||||
|
fetch_reference_bundles=fetch_reference_bundles,
|
||||||
|
auth_type=auth_type,
|
||||||
|
auth_token=auth_token
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in retrieve_bundles: {e}", exc_info=True)
|
||||||
|
yield json.dumps({"type": "error", "message": f"Unexpected error: {str(e)}"}) + "\n"
|
||||||
|
|
||||||
|
response = Response(generate_stream(), mimetype='application/x-ndjson')
|
||||||
|
response.headers['X-Zip-Path'] = os.path.join('/tmp', zip_filename)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/load-ig-to-hapi', methods=['POST'])
|
@app.route('/api/load-ig-to-hapi', methods=['POST'])
|
||||||
|
|||||||
65
services.py
65
services.py
@ -4583,49 +4583,44 @@ def retrieve_bundles(fhir_server_url, resources, output_zip, validate_references
|
|||||||
else:
|
else:
|
||||||
yield json.dumps({"type": "info", "message": "Reference fetching OFF"}) + "\n"
|
yield json.dumps({"type": "info", "message": "Reference fetching OFF"}) + "\n"
|
||||||
|
|
||||||
# Determine Base URL and Headers for Proxy
|
# Determine the final base URL for requests.
|
||||||
base_proxy_url = f"{current_app.config['APP_BASE_URL'].rstrip('/')}/fhir"
|
final_base_url = fhir_server_url.rstrip('/') if fhir_server_url and fhir_server_url != '/fhir' else app.config.get('HAPI_FHIR_URL', 'http://localhost:8080/fhir')
|
||||||
headers = {'Accept': 'application/fhir+json, application/fhir+xml;q=0.9, */*;q=0.8'}
|
headers = {'Accept': 'application/fhir+json, application/fhir+xml;q=0.9, */*;q=0.8'}
|
||||||
|
|
||||||
is_custom_url = fhir_server_url != '/fhir' and fhir_server_url is not None and fhir_server_url.startswith('http')
|
is_custom_url = fhir_server_url != '/fhir' and fhir_server_url is not None and fhir_server_url.startswith('http')
|
||||||
if is_custom_url:
|
if is_custom_url:
|
||||||
# NEW: Add the custom URL as a query parameter for the proxy to use.
|
|
||||||
# This bypasses issues where reverse proxies might strip custom headers.
|
|
||||||
base_proxy_url = f"{base_proxy_url}?proxy-target={fhir_server_url.rstrip('/')}"
|
|
||||||
|
|
||||||
if auth_type in ['bearer', 'basic'] and auth_token:
|
if auth_type in ['bearer', 'basic'] and auth_token:
|
||||||
auth_display = 'Basic <redacted>' if auth_type == 'basic' else (auth_token[:10] + '...' if len(auth_token) > 10 else auth_token)
|
auth_display = 'Basic <redacted>' if auth_type == 'basic' else (auth_token[:10] + '...' if len(auth_token) > 10 else auth_token)
|
||||||
yield json.dumps({"type": "info", "message": f"Using {auth_type} auth with header: Authorization: {auth_display}"}) + "\n"
|
yield json.dumps({"type": "info", "message": f"Using {auth_type} auth with header: Authorization: {auth_display}"}) + "\n"
|
||||||
headers['Authorization'] = auth_token
|
headers['Authorization'] = auth_token
|
||||||
else:
|
else:
|
||||||
yield json.dumps({"type": "info", "message": "Using no authentication for custom URL"}) + "\n"
|
yield json.dumps({"type": "info", "message": "Using no authentication for custom URL"}) + "\n"
|
||||||
logger.debug(f"Will use proxy with proxy-target: {fhir_server_url}")
|
logger.debug(f"Will send requests directly to: {final_base_url}")
|
||||||
else:
|
else:
|
||||||
yield json.dumps({"type": "info", "message": "Using no authentication for local HAPI server"}) + "\n"
|
yield json.dumps({"type": "info", "message": "Using no authentication for local HAPI server"}) + "\n"
|
||||||
logger.debug("Will use proxy targeting local HAPI server")
|
logger.debug("Will use local HAPI server")
|
||||||
|
|
||||||
# Fetch Initial Bundles
|
# Fetch Initial Bundles
|
||||||
initial_bundle_files = []
|
initial_bundle_files = []
|
||||||
for resource_type in resources:
|
for resource_type in resources:
|
||||||
#url = f"{base_proxy_url}/{quote(resource_type)}"
|
url = f"{final_base_url}/{quote(resource_type)}"
|
||||||
url = urljoin(base_proxy_url, quote(resource_type))
|
yield json.dumps({"type": "progress", "message": f"Fetching bundle for {resource_type}..."}) + "\n"
|
||||||
yield json.dumps({"type": "progress", "message": f"Fetching bundle for {resource_type} via proxy..."}) + "\n"
|
logger.debug(f"Sending GET request to {url} with headers: {json.dumps(headers)}")
|
||||||
logger.debug(f"Sending GET request to proxy {url} with headers: {json.dumps(headers)}")
|
|
||||||
try:
|
try:
|
||||||
response = requests.get(url, headers=headers, timeout=60)
|
response = requests.get(url, headers=headers, timeout=60)
|
||||||
logger.debug(f"Proxy response for {resource_type}: HTTP {response.status_code}")
|
logger.debug(f"Response for {resource_type}: HTTP {response.status_code}")
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
error_detail = f"Proxy returned HTTP {response.status_code}."
|
error_detail = f"Server returned HTTP {response.status_code}."
|
||||||
try: error_detail += f" Body: {response.text[:200]}..."
|
try: error_detail += f" Body: {response.text[:200]}..."
|
||||||
except: pass
|
except: pass
|
||||||
yield json.dumps({"type": "error", "message": f"Failed to fetch {resource_type}: {error_detail}"}) + "\n"
|
yield json.dumps({"type": "error", "message": f"Failed to fetch {resource_type}: {error_detail}"}) + "\n"
|
||||||
logger.error(f"Failed to fetch {resource_type} via proxy {url}: {error_detail}")
|
logger.error(f"Failed to fetch {resource_type} at {url}: {error_detail}")
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
bundle = response.json()
|
bundle = response.json()
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
yield json.dumps({"type": "error", "message": f"Invalid JSON response for {resource_type}: {str(e)}"}) + "\n"
|
yield json.dumps({"type": "error", "message": f"Invalid JSON response for {resource_type}: {str(e)}"}) + "\n"
|
||||||
logger.error(f"Invalid JSON from proxy for {resource_type} at {url}: {e}, Response: {response.text[:500]}")
|
logger.error(f"Invalid JSON from {resource_type} at {url}: {e}, Response: {response.text[:500]}")
|
||||||
continue
|
continue
|
||||||
if not isinstance(bundle, dict) or bundle.get('resourceType') != 'Bundle':
|
if not isinstance(bundle, dict) or bundle.get('resourceType') != 'Bundle':
|
||||||
yield json.dumps({"type": "error", "message": f"Expected Bundle for {resource_type}, got {bundle.get('resourceType', 'unknown')}"}) + "\n"
|
yield json.dumps({"type": "error", "message": f"Expected Bundle for {resource_type}, got {bundle.get('resourceType', 'unknown')}"}) + "\n"
|
||||||
@ -4648,8 +4643,8 @@ def retrieve_bundles(fhir_server_url, resources, output_zip, validate_references
|
|||||||
logger.error(f"Failed to write bundle file {output_file}: {e}")
|
logger.error(f"Failed to write bundle file {output_file}: {e}")
|
||||||
continue
|
continue
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
yield json.dumps({"type": "error", "message": f"Error connecting to proxy for {resource_type}: {str(e)}"}) + "\n"
|
yield json.dumps({"type": "error", "message": f"Error connecting to server for {resource_type}: {str(e)}"}) + "\n"
|
||||||
logger.error(f"Error retrieving bundle for {resource_type} via proxy {url}: {e}")
|
logger.error(f"Error retrieving bundle for {resource_type} via {url}: {e}")
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
yield json.dumps({"type": "error", "message": f"Unexpected error fetching {resource_type}: {str(e)}"}) + "\n"
|
yield json.dumps({"type": "error", "message": f"Unexpected error fetching {resource_type}: {str(e)}"}) + "\n"
|
||||||
@ -4699,18 +4694,18 @@ def retrieve_bundles(fhir_server_url, resources, output_zip, validate_references
|
|||||||
if ref_type in retrieved_references_or_types:
|
if ref_type in retrieved_references_or_types:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
url = f"{base_proxy_url}/{quote(ref_type)}"
|
url = f"{final_base_url}/{quote(ref_type)}"
|
||||||
yield json.dumps({"type": "progress", "message": f"Fetching full bundle for type {ref_type} via proxy..."}) + "\n"
|
yield json.dumps({"type": "progress", "message": f"Fetching full bundle for type {ref_type}..."}) + "\n"
|
||||||
logger.debug(f"Sending GET request for full type bundle {ref_type} to proxy {url} with headers: {json.dumps(headers)}")
|
logger.debug(f"Sending GET request for full type bundle {ref_type} to {url} with headers: {json.dumps(headers)}")
|
||||||
try:
|
try:
|
||||||
response = requests.get(url, headers=headers, timeout=180)
|
response = requests.get(url, headers=headers, timeout=180)
|
||||||
logger.debug(f"Proxy response for {ref_type} bundle: HTTP {response.status_code}")
|
logger.debug(f"Response for {ref_type} bundle: HTTP {response.status_code}")
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
error_detail = f"Proxy returned HTTP {response.status_code}."
|
error_detail = f"Server returned HTTP {response.status_code}."
|
||||||
try: error_detail += f" Body: {response.text[:200]}..."
|
try: error_detail += f" Body: {response.text[:200]}..."
|
||||||
except: pass
|
except: pass
|
||||||
yield json.dumps({"type": "warning", "message": f"Failed to fetch full bundle for {ref_type}: {error_detail}"}) + "\n"
|
yield json.dumps({"type": "warning", "message": f"Failed to fetch full bundle for {ref_type}: {error_detail}"}) + "\n"
|
||||||
logger.warning(f"Failed to fetch full bundle {ref_type} via proxy {url}: {error_detail}")
|
logger.warning(f"Failed to fetch full bundle {ref_type} via {url}: {error_detail}")
|
||||||
retrieved_references_or_types.add(ref_type)
|
retrieved_references_or_types.add(ref_type)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -4718,7 +4713,7 @@ def retrieve_bundles(fhir_server_url, resources, output_zip, validate_references
|
|||||||
bundle = response.json()
|
bundle = response.json()
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
yield json.dumps({"type": "warning", "message": f"Invalid JSON for full {ref_type} bundle: {str(e)}"}) + "\n"
|
yield json.dumps({"type": "warning", "message": f"Invalid JSON for full {ref_type} bundle: {str(e)}"}) + "\n"
|
||||||
logger.warning(f"Invalid JSON response from proxy for full {ref_type} bundle at {url}: {e}")
|
logger.warning(f"Invalid JSON response from {ref_type} bundle at {url}: {e}")
|
||||||
retrieved_references_or_types.add(ref_type)
|
retrieved_references_or_types.add(ref_type)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -4742,8 +4737,8 @@ def retrieve_bundles(fhir_server_url, resources, output_zip, validate_references
|
|||||||
logger.error(f"Failed to write full bundle file {output_file}: {e}")
|
logger.error(f"Failed to write full bundle file {output_file}: {e}")
|
||||||
retrieved_references_or_types.add(ref_type)
|
retrieved_references_or_types.add(ref_type)
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
yield json.dumps({"type": "warning", "message": f"Error connecting to proxy for full {ref_type} bundle: {str(e)}"}) + "\n"
|
yield json.dumps({"type": "warning", "message": f"Error connecting to server for full {ref_type} bundle: {str(e)}"}) + "\n"
|
||||||
logger.warning(f"Error retrieving full {ref_type} bundle via proxy: {e}")
|
logger.warning(f"Error retrieving full {ref_type} bundle via: {e}")
|
||||||
retrieved_references_or_types.add(ref_type)
|
retrieved_references_or_types.add(ref_type)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
yield json.dumps({"type": "warning", "message": f"Unexpected error fetching full {ref_type} bundle: {str(e)}"}) + "\n"
|
yield json.dumps({"type": "warning", "message": f"Unexpected error fetching full {ref_type} bundle: {str(e)}"}) + "\n"
|
||||||
@ -4765,19 +4760,19 @@ def retrieve_bundles(fhir_server_url, resources, output_zip, validate_references
|
|||||||
ref_type, ref_id = ref_parts
|
ref_type, ref_id = ref_parts
|
||||||
|
|
||||||
search_param = quote(f"_id={ref_id}")
|
search_param = quote(f"_id={ref_id}")
|
||||||
url = f"{base_proxy_url}/{quote(ref_type)}?{search_param}"
|
url = f"{final_base_url}/{quote(ref_type)}?{search_param}"
|
||||||
yield json.dumps({"type": "progress", "message": f"Fetching referenced {ref_type}/{ref_id} via proxy..."}) + "\n"
|
yield json.dumps({"type": "progress", "message": f"Fetching referenced {ref_type}/{ref_id}..."}) + "\n"
|
||||||
logger.debug(f"Sending GET request for referenced {ref} to proxy {url} with headers: {json.dumps(headers)}")
|
logger.debug(f"Sending GET request for referenced {ref} to {url} with headers: {json.dumps(headers)}")
|
||||||
|
|
||||||
response = requests.get(url, headers=headers, timeout=60)
|
response = requests.get(url, headers=headers, timeout=60)
|
||||||
logger.debug(f"Proxy response for referenced {ref}: HTTP {response.status_code}")
|
logger.debug(f"Response for referenced {ref}: HTTP {response.status_code}")
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
error_detail = f"Proxy returned HTTP {response.status_code}."
|
error_detail = f"Server returned HTTP {response.status_code}."
|
||||||
try: error_detail += f" Body: {response.text[:200]}..."
|
try: error_detail += f" Body: {response.text[:200]}..."
|
||||||
except: pass
|
except: pass
|
||||||
yield json.dumps({"type": "warning", "message": f"Failed to fetch referenced {ref}: {error_detail}"}) + "\n"
|
yield json.dumps({"type": "warning", "message": f"Failed to fetch referenced {ref}: {error_detail}"}) + "\n"
|
||||||
logger.warning(f"Failed to fetch referenced {ref} via proxy {url}: {error_detail}")
|
logger.warning(f"Failed to fetch referenced {ref} via {url}: {error_detail}")
|
||||||
retrieved_references_or_types.add(ref)
|
retrieved_references_or_types.add(ref)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -4785,7 +4780,7 @@ def retrieve_bundles(fhir_server_url, resources, output_zip, validate_references
|
|||||||
bundle = response.json()
|
bundle = response.json()
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
yield json.dumps({"type": "warning", "message": f"Invalid JSON for referenced {ref}: {str(e)}"}) + "\n"
|
yield json.dumps({"type": "warning", "message": f"Invalid JSON for referenced {ref}: {str(e)}"}) + "\n"
|
||||||
logger.warning(f"Invalid JSON from proxy for ref {ref} at {url}: {e}")
|
logger.warning(f"Invalid JSON from ref {ref} at {url}: {e}")
|
||||||
retrieved_references_or_types.add(ref)
|
retrieved_references_or_types.add(ref)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -4815,7 +4810,7 @@ def retrieve_bundles(fhir_server_url, resources, output_zip, validate_references
|
|||||||
retrieved_references_or_types.add(ref)
|
retrieved_references_or_types.add(ref)
|
||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
yield json.dumps({"type": "warning", "message": f"Network error fetching referenced {ref}: {str(e)}"}) + "\n"
|
yield json.dumps({"type": "warning", "message": f"Network error fetching referenced {ref}: {str(e)}"}) + "\n"
|
||||||
logger.warning(f"Network error retrieving referenced {ref} via proxy: {e}")
|
logger.warning(f"Network error retrieving referenced {ref} via: {e}")
|
||||||
retrieved_references_or_types.add(ref)
|
retrieved_references_or_types.add(ref)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
yield json.dumps({"type": "warning", "message": f"Unexpected error fetching referenced {ref}: {str(e)}"}) + "\n"
|
yield json.dumps({"type": "warning", "message": f"Unexpected error fetching referenced {ref}: {str(e)}"}) + "\n"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user