From e04e09a2ae40b2671354198a3fe12efb47986df4 Mon Sep 17 00:00:00 2001 From: rkorytkowski Date: Wed, 1 May 2024 12:09:43 +0200 Subject: [PATCH] OpenConceptLab/ocl_issues#1761 Add FHIR xml support --- core/common/renderers.py | 4 ++++ core/middlewares/middlewares.py | 38 ++++++++++++++++++++++++++++----- core/settings.py | 3 +++ docker-compose.yml | 8 +++++++ 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/core/common/renderers.py b/core/common/renderers.py index 13a95357f..9163f973d 100644 --- a/core/common/renderers.py +++ b/core/common/renderers.py @@ -20,3 +20,7 @@ def render(self, data, accepted_media_type=None, renderer_context=None): wrapper = FileWrapper(temp) temp.seek(0) return wrapper + + +class FhirRenderer(JSONRenderer): + media_type = 'application/fhir+json' \ No newline at end of file diff --git a/core/middlewares/middlewares.py b/core/middlewares/middlewares.py index ced5ec87a..ca9dcddf4 100644 --- a/core/middlewares/middlewares.py +++ b/core/middlewares/middlewares.py @@ -1,7 +1,8 @@ import logging import time -from django.http import HttpResponseNotFound +import requests +from django.http import HttpResponseNotFound, HttpRequest, HttpResponse from request_logging.middleware import LoggingMiddleware from core.common.constants import VERSION_HEADER, REQUEST_USER_HEADER, RESPONSE_TIME_HEADER, REQUEST_URL_HEADER, \ @@ -83,14 +84,16 @@ def __call__(self, request): class FhirMiddleware(BaseMiddleware): """ - It is used to expose FHIR endpoints under FHIR subdomain only. If FHIR is not deployed under a dedicated subdomain - then FHIR_SUBDOMAIN environment variable should be empty. + It is used to expose FHIR endpoints under FHIR subdomain only and convert content from xml to json. + If FHIR is not deployed under a dedicated subdomain then FHIR_SUBDOMAIN environment variable should be empty. """ def __call__(self, request): + absolute_uri = request.build_absolute_uri() + from django.conf import settings if settings.FHIR_SUBDOMAIN: - uri = request.build_absolute_uri().split('/') + uri = absolute_uri.split('/') domain = uri[2] if len(uri) > 2 else '' is_fhir_domain = domain.startswith(settings.FHIR_SUBDOMAIN + '.') resource_type = uri[5] if len(uri) > 5 else None @@ -104,5 +107,30 @@ def __call__(self, request): elif is_fhir_resource: return HttpResponseNotFound() - response = self.get_response(request) + if settings.FHIR_VALIDATOR_URL and ('/CodeSystem/' in absolute_uri or '/ValueSet/' in absolute_uri or + '/ConceptMap' in absolute_uri): + accept_content_type = request.headers.get('Accept') + content_type = request.headers.get('Content-Type') + + if content_type.startswith('application/xml') or content_type.startswith('application/fhir+xml'): + request.META['CONTENT_TYPE'] = "application/json" + if request.method == 'POST' or request.method == 'PUT': + json_request = requests.post(settings.FHIR_VALIDATOR_URL + + '/convert?version=4.0&type=xml&toType=json', data=request.body) + request._body = json_request + + if accept_content_type.startswith('application/xml') or \ + accept_content_type.startswith('application/fhir+xml'): + request.META['HTTP_ACCEPT'] = "application/json" + + response = self.get_response(request) + + if accept_content_type.startswith('application/xml') or \ + accept_content_type.startswith('application/fhir+xml'): + xml_response = requests.post(settings.FHIR_VALIDATOR_URL + '/convert?version=4.0&type=json&toType=xml', + data=response.content) + response = HttpResponse(xml_response, content_type=accept_content_type) + else: + response = self.get_response(request) + return response diff --git a/core/settings.py b/core/settings.py index 161970dbc..ab7af6e5b 100644 --- a/core/settings.py +++ b/core/settings.py @@ -114,6 +114,7 @@ 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'core.common.renderers.ZippedJSONRenderer', + 'core.common.renderers.FhirRenderer' ), 'COERCE_DECIMAL_TO_STRING': False, 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', @@ -337,6 +338,8 @@ API_SUPERUSER_PASSWORD = os.environ.get('API_SUPERUSER_PASSWORD', 'Root123') # password for ocladmin superuser API_SUPERUSER_TOKEN = os.environ.get('API_SUPERUSER_TOKEN', '891b4b17feab99f3ff7e5b5d04ccc5da7aa96da6') +FHIR_VALIDATOR_URL = os.environ.get('FHIR_VALIDATOR_URL', None) + # Redis REDIS_CONNECTION_OPTIONS = { 'socket_timeout': 5.0, diff --git a/docker-compose.yml b/docker-compose.yml index 55726087d..3f0d03605 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,8 +54,16 @@ services: - AZURE_STORAGE_ACCOUNT_NAME - AZURE_STORAGE_CONTAINER_NAME - AZURE_STORAGE_CONNECTION_STRING + - FHIR_VALIDATOR_URL=${FHIR_VALIDATOR_URL-http://fhir_validator:3500} healthcheck: test: "curl --silent --fail http://localhost:8000/version/ || exit 1" + fhir_validator: + image: openconceptlab/validator-wrapper:1.0.53-1deb1a4b + restart: "always" + ports: + - 3500:3500 + healthcheck: + test: "curl --silent --fail http://localhost:3500/ || exit 1" celery: image: openconceptlab/oclapi2:${ENVIRONMENT-production} command: ["bash", "-c", "CELERY_WORKER_NAME=default ./start_celery_worker.sh -P prefork -Q default -c 2"]