Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 21 additions & 155 deletions sandbox/api/app.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,18 @@
from logging import INFO, basicConfig, getLogger
from typing import Union
from flask import Flask, request

from .constants import (
INTERNAL_SERVER_ERROR_EXAMPLE,
QUESTIONNAIRE_RESPONSE__SUCCESS,
RELATED__LIST_RELATIONSHIP,
RELATED__LIST_RELATIONSHIP_WITH_INCLUDE,
RELATED__VERIFY_RELATIONSHIP_09,
RELATED__VERIFY_RELATIONSHIP_09_WITH_INCLUDE,
RELATED__VERIFY_RELATIONSHIP_25,
RELATED__VERIFY_RELATIONSHIP_25_WITH_INCLUDE,
POST_CONSENT__SUCCESS,
POST_CONSENT__DUPLICATE_RELATIONSHIP_ERROR,
POST_CONSENT__PERFORMER_IDENTIFIER_ERROR,
PATCH_CONSENT__SUCCESS,
PATCH_CONSENT__INVALID_PATCH_FORMAT,
PATCH_CONSENT__INVALID_PATH,
PATCH_CONSENT__INVALID_STATUS_CODE,
PATCH_CONSENT__RESOURCE_NOT_FOUND,
PATCH_CONSENT__INVALID_STATE_TRANSITION,
)

from flask import Flask

from .get_consent import get_consent_response
from .utils import (
check_for_empty,
check_for_list,
check_for_related_person_errors,
check_for_validate,
generate_response_from_example,
remove_system,
)
from .get_related_person import get_related_person_response
from .patch_consent import patch_consent_response
from .post_consent import post_consent_response
from .post_questionnaire_response import post_questionnaire_response_response

app = Flask(__name__)
basicConfig(level=INFO, format="%(asctime)s - %(message)s")
logger = getLogger(__name__)
APP_BASE_PATH = "https://sandbox.api.service.nhs.uk/validated-relationships/FHIR/R4/Consent"

COMMON_PATH = "FHIR/R4"


Expand All @@ -56,54 +34,7 @@ def get_related_persons() -> Union[dict, tuple]:
Returns:
Union[dict, tuple]: Response for GET /RelatedPerson
"""

try:
# Check Headers
if errors := check_for_related_person_errors(request):
return errors

identifier = remove_system(request.args.get("identifier"))
patient_identifier = remove_system(request.args.get("patient:identifier"))
include = request.args.get("_include")

if empty := check_for_empty(identifier, patient_identifier):
return empty

# Successful request, select response
if zero_nine := check_for_validate(
"9000000009",
identifier,
patient_identifier,
include,
RELATED__VERIFY_RELATIONSHIP_09,
RELATED__VERIFY_RELATIONSHIP_09_WITH_INCLUDE,
):
return zero_nine

if two_five := check_for_validate(
"9000000025",
identifier,
patient_identifier,
include,
RELATED__VERIFY_RELATIONSHIP_25,
RELATED__VERIFY_RELATIONSHIP_25_WITH_INCLUDE,
):
return two_five

if one_seven := check_for_list(
"9000000017",
identifier,
include,
RELATED__LIST_RELATIONSHIP,
RELATED__LIST_RELATIONSHIP_WITH_INCLUDE,
):
return one_seven

raise ValueError("Invalid request")

except Exception:
logger.exception("GET related person failed")
return generate_response_from_example(INTERNAL_SERVER_ERROR_EXAMPLE, 500)
return get_related_person_response()


@app.route(f"/{COMMON_PATH}/QuestionnaireResponse", methods=["POST"])
Expand All @@ -113,12 +44,7 @@ def post_questionnaire_response() -> Union[dict, tuple]:
Returns:
Union[dict, tuple]: Response for POST /QuestionnaireResponse
"""

try:
return generate_response_from_example(QUESTIONNAIRE_RESPONSE__SUCCESS, 200)
except Exception:
logger.exception("POST questionnaire response failed")
return generate_response_from_example(INTERNAL_SERVER_ERROR_EXAMPLE, 500)
return post_questionnaire_response_response()


@app.route(f"/{COMMON_PATH}/Consent", methods=["GET"])
Expand All @@ -138,77 +64,17 @@ def post_consent() -> Union[dict, tuple]:
Returns:
Union[dict, tuple]: Response for POST /Consent
"""
try:
logger.debug("Received request to POST consent")
# Validate body - beyond the scope of sandbox - assume body is valid for scenario
json = request.get_json()
patient_identifier = json["performer"][0]["identifier"]["value"]
response = None

# Successful parent-child proxy creation
# Successful adult-adult proxy creation
if patient_identifier == "9000000009" or patient_identifier == "9000000017":
header = {"location": f"{APP_BASE_PATH}/{patient_identifier}"}
response = generate_response_from_example(POST_CONSENT__SUCCESS, 201, headers=header)

# Duplicate relationship
elif patient_identifier == "9000000049":
response = generate_response_from_example(POST_CONSENT__DUPLICATE_RELATIONSHIP_ERROR, 409)
# Invalid performer NHS number
elif patient_identifier == "9000000000":
response = generate_response_from_example(POST_CONSENT__PERFORMER_IDENTIFIER_ERROR, 422)
else:
# Out of scope errors
raise ValueError("Invalid Request")

return response

except Exception:
# Handle any general error
logger.exception("POST Consent failed")
return generate_response_from_example(INTERNAL_SERVER_ERROR_EXAMPLE, 500)
return post_consent_response()


@app.route(f"/{COMMON_PATH}/Consent/<identifier>", methods=["PATCH"])
def patch_consent(identifier) -> Union[dict, tuple]:
try:
logger.debug("Received request to PATCH consent")
# Validate body - validation is beyond the scope of the sandbox.
# Assume all requests are valid

if identifier == "c6f48e4d":
# Successful status update
return generate_response_from_example(PATCH_CONSENT__SUCCESS, 200)

elif identifier == "0c56a594":
# Successful end date for a role
return generate_response_from_example(PATCH_CONSENT__SUCCESS, 200)

elif identifier == "b02ea26c":
# Multiple valid changes
return generate_response_from_example(PATCH_CONSENT__SUCCESS, 200)

elif identifier == "3a2679eb":
# Invalid patch format
return generate_response_from_example(PATCH_CONSENT__INVALID_PATCH_FORMAT, 400)

elif identifier == "94df7c8f":
# Invalid path
return generate_response_from_example(PATCH_CONSENT__INVALID_PATH, 400)

elif identifier == "2a7b736d":
# Invalid status code
return generate_response_from_example(PATCH_CONSENT__INVALID_STATUS_CODE, 422)

elif identifier == "6fb4361b":
# Invalid state transition
return generate_response_from_example(PATCH_CONSENT__INVALID_STATE_TRANSITION, 422)

else:
# Resource not found
return generate_response_from_example(PATCH_CONSENT__RESOURCE_NOT_FOUND, 404)

except Exception:
# Handle any general error
logger.exception("PATCH Consent failed")
return generate_response_from_example(INTERNAL_SERVER_ERROR_EXAMPLE, 500)
def patch_consent(identifier: str) -> Union[dict, tuple]:
"""Sandbox API for PATCH /Consent

Args:
identifier (str): Consent identifier to be patched

Returns:
Union[dict, tuple]: Response for PATCH /Consent
"""
return patch_consent_response(identifier)
8 changes: 4 additions & 4 deletions sandbox/api/get_consent.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
from flask import request

from .constants import (
INTERNAL_SERVER_ERROR_EXAMPLE,
INVALIDATED_RESOURCE,
GET_CONSENT__FILTERED_RELATIONSHIPS_STATUS_ACTIVE,
GET_CONSENT__FILTERED_RELATIONSHIPS_STATUS_INACTIVE,
GET_CONSENT__FILTERED_RELATIONSHIPS_STATUS_PROPOSED_ACTIVE,
Expand All @@ -18,9 +16,11 @@
GET_CONSENT__SINGLE_CONSENTING_ADULT_RELATIONSHIP_INCLUDE_BOTH,
GET_CONSENT__SINGLE_MOTHER_CHILD_RELATIONSHIP,
GET_CONSENT__SINGLE_MOTHER_CHILD_RELATIONSHIP_INCLUDE_BOTH,
INTERNAL_SERVER_ERROR_EXAMPLE,
INVALIDATED_RESOURCE,
)
from .utils import (
check_for_consent_errors,
check_for_get_consent_errors,
check_for_consent_filtering,
check_for_consent_include_params,
generate_response_from_example,
Expand All @@ -39,7 +39,7 @@ def get_consent_response() -> Union[dict, tuple]:
"""
try:
# Check Headers
if errors := check_for_consent_errors(request):
if errors := check_for_get_consent_errors(request):
return errors

performer_identifier = remove_system(request.args.get("performer:identifier"))
Expand Down
80 changes: 80 additions & 0 deletions sandbox/api/get_related_person.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from logging import INFO, basicConfig, getLogger
from typing import Union

from flask import request

from .constants import (
INTERNAL_SERVER_ERROR_EXAMPLE,
RELATED__LIST_RELATIONSHIP,
RELATED__LIST_RELATIONSHIP_WITH_INCLUDE,
RELATED__VERIFY_RELATIONSHIP_09,
RELATED__VERIFY_RELATIONSHIP_09_WITH_INCLUDE,
RELATED__VERIFY_RELATIONSHIP_25,
RELATED__VERIFY_RELATIONSHIP_25_WITH_INCLUDE,
)
from .utils import (
check_for_empty,
check_for_get_related_person_errors,
check_for_list,
check_for_validate,
generate_response_from_example,
remove_system,
)

basicConfig(level=INFO, format="%(asctime)s - %(message)s")
logger = getLogger(__name__)


def get_related_person_response() -> Union[dict, tuple]:
"""Sandbox API for GET /RelatedPerson

Returns:
Union[dict, tuple]: Response for GET /RelatedPerson
"""
try:
# Check Headers
if errors := check_for_get_related_person_errors(request):
return errors

identifier = remove_system(request.args.get("identifier"))
patient_identifier = remove_system(request.args.get("patient:identifier"))
include = request.args.get("_include")

if empty := check_for_empty(identifier, patient_identifier):
return empty

# Successful request, select response
if zero_nine := check_for_validate(
"9000000009",
identifier,
patient_identifier,
include,
RELATED__VERIFY_RELATIONSHIP_09,
RELATED__VERIFY_RELATIONSHIP_09_WITH_INCLUDE,
):
return zero_nine

if two_five := check_for_validate(
"9000000025",
identifier,
patient_identifier,
include,
RELATED__VERIFY_RELATIONSHIP_25,
RELATED__VERIFY_RELATIONSHIP_25_WITH_INCLUDE,
):
return two_five

if one_seven := check_for_list(
"9000000017",
identifier,
include,
RELATED__LIST_RELATIONSHIP,
RELATED__LIST_RELATIONSHIP_WITH_INCLUDE,
):
return one_seven

raise ValueError("Invalid request")

except Exception:
logger.exception("GET related person failed")
return generate_response_from_example(INTERNAL_SERVER_ERROR_EXAMPLE, 500)
68 changes: 68 additions & 0 deletions sandbox/api/patch_consent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from logging import INFO, basicConfig, getLogger
from typing import Union

from .constants import (
INTERNAL_SERVER_ERROR_EXAMPLE,
PATCH_CONSENT__INVALID_PATCH_FORMAT,
PATCH_CONSENT__INVALID_PATH,
PATCH_CONSENT__INVALID_STATE_TRANSITION,
PATCH_CONSENT__INVALID_STATUS_CODE,
PATCH_CONSENT__RESOURCE_NOT_FOUND,
PATCH_CONSENT__SUCCESS,
)
from .utils import generate_response_from_example

basicConfig(level=INFO, format="%(asctime)s - %(message)s")
logger = getLogger(__name__)


def patch_consent_response(identifier: str) -> Union[dict, tuple]:
"""Sandbox API for PATCH /Consent

Args:
identifier (str): Consent identifier to be patched

Returns:
Union[dict, tuple]: Response for PATCH /Consent
"""
try:
logger.debug("Received request to PATCH consent")
# Validate body - validation is beyond the scope of the sandbox.
# Assume all requests are valid

if identifier == "c6f48e4d":
# Successful status update
return generate_response_from_example(PATCH_CONSENT__SUCCESS, 200)

elif identifier == "0c56a594":
# Successful end date for a role
return generate_response_from_example(PATCH_CONSENT__SUCCESS, 200)

elif identifier == "b02ea26c":
# Multiple valid changes
return generate_response_from_example(PATCH_CONSENT__SUCCESS, 200)

elif identifier == "3a2679eb":
# Invalid patch format
return generate_response_from_example(PATCH_CONSENT__INVALID_PATCH_FORMAT, 400)

elif identifier == "94df7c8f":
# Invalid path
return generate_response_from_example(PATCH_CONSENT__INVALID_PATH, 400)

elif identifier == "2a7b736d":
# Invalid status code
return generate_response_from_example(PATCH_CONSENT__INVALID_STATUS_CODE, 422)

elif identifier == "6fb4361b":
# Invalid state transition
return generate_response_from_example(PATCH_CONSENT__INVALID_STATE_TRANSITION, 422)

else:
# Resource not found
return generate_response_from_example(PATCH_CONSENT__RESOURCE_NOT_FOUND, 404)

except Exception:
# Handle any general error
logger.exception("PATCH Consent failed")
return generate_response_from_example(INTERNAL_SERVER_ERROR_EXAMPLE, 500)
Loading