Skip to content

Commit

Permalink
POC passthrough on backend FHIR OperationOutcome response.
Browse files Browse the repository at this point in the history
  • Loading branch information
JFU-NAVA-PBC committed Jan 13, 2025
1 parent 6ae222f commit f05539d
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 23 deletions.
6 changes: 6 additions & 0 deletions apps/authorization/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ def has_object_permission(self, request, view, obj):


def is_resource_for_patient(obj, patient_id):

if (obj is not None
and obj.get('resourceType') is not None
and obj.get('resourceType') == 'OperationOutcome'):
return True

try:
if obj['resourceType'] == 'Coverage':
reference = obj['beneficiary']['reference']
Expand Down
67 changes: 47 additions & 20 deletions apps/fhir/bluebutton/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,68 @@
from requests.exceptions import JSONDecodeError
from .models import Fhir_Response

# 1xx informational response – the request was received, continuing process
# 2xx successful – the request was successfully received, understood, and accepted
# 3xx redirection – further action needs to be taken in order to complete the request
# 4xx client error – the request contains bad syntax or cannot be fulfilled
# 5xx server error – the server failed to fulfil an apparently valid request

def process_error_response(response: Fhir_Response) -> APIException:
"""
""" Need to be revised to new handling logic
Process errors coming from FHIR endpoints.
All status codes in 400s except 404 get wrapped in
BadRequestError with diagnostics message attached if found.
If not found, Generic UpstreamServerException will be returned.
404 returns NotFound error.
500 messages will return generic message with no diagnostics.
"""
err: APIException = None
r: Response = response.backend_response
if response.status_code >= 300:
"""
Process errors coming from FHIR endpoints.
Response with 404: throw NOTFOUND
Any backend response which is a FHIR OperationOutcome will be passed through.
For anything else, A UpstreamServerException will be returned with HTTP code 502.
"""
if response.status_code in range(200, 400):
# 2xx, 3xx - return to caller real quick
return None
else:
err: APIException = None
r: Response = response.backend_response
if response.status_code == 404:
# not found is not bad gateway
err = NotFound('The requested resource does not exist')
elif (response.status_code in range(400, 600) and
not _is_operation_outcome(r)):
# only back end response with 4xx, 5xx and the payload is not FHIR OperationOutcome
# are treated as bad gateway
# TODO: add more back end response content to the BAD GATEWAY message?
# it helps trouble shooting, concern: would there be PII/PHI/sensitive info in the back end response?
err = UpstreamServerException('An error occurred contacting the upstream server')
else:
msg = 'An error occurred contacting the upstream server'
err = UpstreamServerException(msg)
if response.status_code in range(400, 500):
try:
json = r.json()
if json is not None:
issues = json.get('issue')
issue = issues[0] if issues else None
diagnostics = issue.get('diagnostics') if issue else None
if diagnostics is not None:
err = BadRequestToBackendError("{}:{}".format(msg, diagnostics))
except JSONDecodeError:
pass
return err
# 1xx - rare case, proceed to caller
pass
return err


class UpstreamServerException(APIException):
status_code = status.HTTP_502_BAD_GATEWAY

#
# class BadRequestToBackendError(APIException):
# status_code = status.HTTP_400_BAD_REQUEST
#

class BadRequestToBackendError(APIException):
status_code = status.HTTP_400_BAD_REQUEST
# helper to check if the response is a FHIR OperationOutcome
def _is_operation_outcome(r: Fhir_Response):
result = False
try:
json = r.json()
result = (json is not None
and json.get('resourceType') is not None
and json.get('resourceType') == 'OperationOutcome')
except JSONDecodeError:
# the body is not a json
# TODO: consider debug logging the body content (e.g. a html string)
# to help trouble shooting
pass
return result
5 changes: 5 additions & 0 deletions apps/fhir/bluebutton/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ def has_object_permission(self, request, view, obj):
# Patient resources were taken care of above
# Return 404 on error to avoid notifying unauthorized user the object exists

if (obj is not None
and obj.get('resourceType') is not None
and obj.get('resourceType') == 'OperationOutcome'):
return True

try:
if request.resource_type == "Coverage":
reference = obj["beneficiary"]["reference"]
Expand Down
6 changes: 3 additions & 3 deletions apps/fhir/bluebutton/views/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ def initial(self, request, resource_type, *args, **kwargs):

def get(self, request, resource_type, *args, **kwargs):

out_data = self.fetch_data(request, resource_type, *args, **kwargs)
out_data, status_code = self.fetch_data(request, resource_type, *args, **kwargs)

return Response(out_data)
return Response(out_data,status_code)

def fetch_data(self, request, resource_type, *args, **kwargs):
resource_router = get_resourcerouter(request.crosswalk)
Expand Down Expand Up @@ -175,4 +175,4 @@ def fetch_data(self, request, resource_type, *args, **kwargs):

self.check_object_permissions(request, out_data)

return out_data
return out_data, response.status_code

0 comments on commit f05539d

Please sign in to comment.