Skip to content

Commit c3cba3f

Browse files
authored
Merge pull request #127 from NHSDigital/dev/NPA-3899_Sandbox_Consent_Endpoint
NPA-3899 Add Consent Sandbox Endpoint
2 parents b350879 + 2f1c794 commit c3cba3f

22 files changed

+536
-267
lines changed

Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM python:3.8
2+
3+
COPY ./specification/examples/responses /sandbox/api/examples
4+
COPY ./sandbox /sandbox
5+
6+
WORKDIR /sandbox
7+
8+
RUN pip install poetry && poetry install --without dev
9+
10+
EXPOSE 9000
11+
12+
CMD ["poetry", "run", "gunicorn", "api.app:app", "--bind=0.0.0.0:9000"]

ecs-proxies-containers.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
docker_containers:
22
- name: sandbox
3-
dockerfile: "sandbox/Dockerfile"
4-
path: "sandbox"
3+
dockerfile: "Dockerfile"
4+
path: "."

sandbox/.dockerignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

sandbox/Dockerfile

Lines changed: 0 additions & 13 deletions
This file was deleted.

sandbox/Makefile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ list:
88
@grep '^[^#[:space:]].*:' Makefile
99

1010
build: # Build the Docker image to simulate ECS build
11-
docker build -t validate-relationships-service-api-sandbox .
11+
cd .. && docker build -t validate-relationships-service-api-sandbox .
12+
13+
load-examples: # Load the examples from the specification (for local development, not used by docker build)
14+
cp -r ../specification/examples/responses/ ./api/examples
1215

1316
format: # Formats the code using black
1417
poetry run black .
1518

16-
start: # Starts the Flask app (Don't use this in production)
19+
start: load-examples # Starts the Flask app (Don't use this in production)
1720
poetry run flask --app api.app:app run -p 9000
1821

19-
start-dev: # Starts the Flask app in development mode
22+
start-dev: load-examples # Starts the Flask app in development mode
2023
poetry run flask --app api.app:app run -p 9000 --reload --debug
2124

2225
test: # Runs the unit tests using pytest

sandbox/README.md

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,27 @@
22

33
This folder contains a sandbox API for initial development
44

5-
6-
For more information about building sandbox APIs see the [API Producer Zone confluence](https://nhsd-confluence.digital.nhs.uk/display/APM/Setting+up+your+API+sandbox ).
5+
For more information about building sandbox APIs see the [API Producer Zone confluence](https://nhsd-confluence.digital.nhs.uk/display/APM/Setting+up+your+API+sandbox).
76

87
## Table of Contents
98

10-
- [Sandbox](#sandbox)
11-
- [Table of Contents](#table-of-contents)
12-
- [Prerequisites](#prerequisites)
13-
- [Quick Start](#quick-start)
14-
- [Installing dependencies](#installing-dependencies)
15-
- [Starting the API](#starting-the-api)
16-
- [Development](#development)
17-
- [Starting the API with Hot Reloading](#starting-the-api-with-hot-reloading)
18-
- [Testing](#testing)
19-
- [Unit Tests](#unit-tests)
20-
- [Useful commands](#useful-commands)
9+
- [Sandbox](#sandbox)
10+
- [Table of Contents](#table-of-contents)
11+
- [Prerequisites](#prerequisites)
12+
- [Quick Start](#quick-start)
13+
- [Installing dependencies](#installing-dependencies)
14+
- [Starting the API](#starting-the-api)
15+
- [Development](#development)
16+
- [Starting the API with Hot Reloading](#starting-the-api-with-hot-reloading)
17+
- [Testing](#testing)
18+
- [Unit Tests](#unit-tests)
19+
- [Useful commands](#useful-commands)
2120

2221
## Prerequisites
2322

24-
- Python 3.8
25-
- [Poetry](https://python-poetry.org/docs/)
26-
- [Docker](https://docs.docker.com/get-docker/)
23+
- Python 3.8
24+
- [Poetry](https://python-poetry.org/docs/)
25+
- [Docker](https://docs.docker.com/get-docker/)
2726

2827
## Quick Start
2928

@@ -53,7 +52,6 @@ To run the API with hot reloading use `make start-dev`
5352

5453
Unit tests can be run using `make test`
5554

56-
5755
### Useful commands
5856

5957
For more useful development commands review the [Makefile](Makefile) or run `make list`

sandbox/api/app.py

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,29 @@
33

44
from flask import Flask, request
55

6-
from .utils import (
7-
ERROR_RESPONSE,
6+
from .constants import (
7+
CONSENT__ADULT_CONSENTING_EXAMPLE,
8+
CONSENT__MIXED_EXAMPLE,
9+
CONSENT__MOTHER_CHILD_EXAMPLE,
10+
CONSENT_PERFORMER,
11+
INTERNAL_ERROR_RESPONSE,
12+
INTERNAL_SERVER_ERROR_EXAMPLE,
813
LIST_RELATIONSHIP,
914
LIST_RELATIONSHIP_INCLUDE,
15+
NOT_FOUND,
1016
QUESTIONNAIRE_RESPONSE_SUCCESS,
1117
VALIDATE_RELATIONSHIP_009,
1218
VALIDATE_RELATIONSHIP_025,
1319
VALIDATE_RELATIONSHIP_INCLUDE_009,
1420
VALIDATE_RELATIONSHIP_INCLUDE_025,
21+
)
22+
from .utils import (
1523
check_for_empty,
1624
check_for_errors,
1725
check_for_list,
1826
check_for_validate,
1927
generate_response,
28+
generate_response_from_example,
2029
load_json_file,
2130
remove_system,
2231
)
@@ -92,7 +101,7 @@ def get_related_persons() -> Union[dict, tuple]:
92101

93102
except Exception as e:
94103
logger.error(e)
95-
return generate_response(load_json_file(ERROR_RESPONSE), 500)
104+
return generate_response(load_json_file(INTERNAL_ERROR_RESPONSE), 500)
96105

97106

98107
@app.route(f"/{COMMON_PATH}/QuestionnaireResponse", methods=["POST"])
@@ -107,4 +116,43 @@ def post_questionnaire_response() -> Union[dict, tuple]:
107116
return generate_response(load_json_file(QUESTIONNAIRE_RESPONSE_SUCCESS), 200)
108117
except Exception as e:
109118
logger.error(e)
110-
return generate_response(load_json_file(ERROR_RESPONSE), 500)
119+
return generate_response(load_json_file(INTERNAL_ERROR_RESPONSE), 500)
120+
121+
122+
@app.route(f"/{COMMON_PATH}/Consent", methods=["GET"])
123+
def get_consent() -> Union[dict, tuple]:
124+
"""Sandbox API for GET /Consent
125+
126+
Returns:
127+
Union[dict, tuple]: Response for GET /Consent
128+
"""
129+
try:
130+
performer_identifier = remove_system(request.args.get("performer:identifier"))
131+
status = request.args.get("status")
132+
_include = request.args.get("_include")
133+
134+
if (
135+
performer_identifier == "9000000010"
136+
and status == "active"
137+
and _include == CONSENT_PERFORMER
138+
):
139+
return generate_response_from_example(
140+
CONSENT__ADULT_CONSENTING_EXAMPLE, 200
141+
)
142+
elif (
143+
performer_identifier == "9000000017"
144+
and status == "active"
145+
and _include == CONSENT_PERFORMER
146+
):
147+
return generate_response_from_example(CONSENT__MIXED_EXAMPLE, 200)
148+
elif (
149+
performer_identifier == "9000000019"
150+
and status == "active"
151+
and _include == CONSENT_PERFORMER
152+
):
153+
return generate_response_from_example(CONSENT__MOTHER_CHILD_EXAMPLE, 200)
154+
else:
155+
return generate_response(load_json_file(NOT_FOUND), 400)
156+
except Exception as e:
157+
logger.error(e)
158+
return generate_response_from_example(INTERNAL_SERVER_ERROR_EXAMPLE, 500)

sandbox/api/constants.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
NOT_FOUND = "./api/responses/not_found.json"
2+
EMPTY_RESPONSE = "./api/responses/GET_RelatedPerson/empty_response_9000000033.json"
3+
LIST_RELATIONSHIP = (
4+
"./api/responses/GET_RelatedPerson/list_relationship_9000000017.json"
5+
)
6+
LIST_RELATIONSHIP_INCLUDE = (
7+
"./api/responses/GET_RelatedPerson/list_relationship_include_9000000017.json"
8+
)
9+
VALIDATE_RELATIONSHIP_009 = (
10+
"./api/responses/GET_RelatedPerson/verify_relationship_9000000009.json"
11+
)
12+
VALIDATE_RELATIONSHIP_INCLUDE_009 = (
13+
"./api/responses/GET_RelatedPerson/verify_relationship_include_9000000009.json"
14+
)
15+
VALIDATE_RELATIONSHIP_025 = (
16+
"./api/responses/GET_RelatedPerson/verify_relationship_9000000025.json"
17+
)
18+
VALIDATE_RELATIONSHIP_INCLUDE_025 = (
19+
"./api/responses/GET_RelatedPerson/verify_relationship_include_9000000025.json"
20+
)
21+
INTERNAL_ERROR_RESPONSE = "./api/responses/internal_server_error.json"
22+
INCLUDE_FLAG = "RelatedPerson:patient"
23+
24+
QUESTIONNAIRE_RESPONSE_SUCCESS = (
25+
"./api/responses/POST_QuestionnaireResponse/questionnaire_response_success.json"
26+
)
27+
28+
PATIENT_IDENTIFIERS = ["9000000017", "9000000033"]
29+
RELATED_IDENTIFIERS = ["9000000009", "9000000025"]
30+
31+
CONSENT_PERFORMER = "Consent:performer"
32+
33+
# Example files
34+
35+
# Common examples
36+
INTERNAL_SERVER_ERROR_EXAMPLE = "./api/examples/errors/internal-server-error.yaml"
37+
# Consent examples
38+
CONSENT__ADULT_CONSENTING_EXAMPLE = "./api/examples/GET_Consent/adults-consenting.yaml"
39+
CONSENT__MIXED_EXAMPLE = "./api/examples/GET_Consent/mixed.yaml"
40+
CONSENT__MOTHER_CHILD_EXAMPLE = "./api/examples/GET_Consent/mother-child.yaml"

sandbox/api/examples/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.yaml
Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
{
22
"issue": [
3-
{
4-
"code": "invalid",
5-
"details": {
6-
"coding": [
7-
{
8-
"code": "INVALID_IDENTIFIER_SYSTEM",
9-
"display": "Invalid identifier system.",
10-
"system": "https://fhir.nhs.uk/R4/CodeSystem/ValidatedRelationships-ErrorOrWarningCode",
11-
"version": "1"
12-
}
13-
]
14-
},
15-
"diagnostics": "The identifier system is not valid.",
16-
"severity": "error"
17-
}
3+
{
4+
"code": "invalid",
5+
"details": {
6+
"coding": [
7+
{
8+
"code": "INVALID_IDENTIFIER_SYSTEM",
9+
"display": "Invalid identifier system.",
10+
"system": "https://fhir.nhs.uk/R4/CodeSystem/ValidatedRelationships-ErrorOrWarningCode",
11+
"version": "1"
12+
}
13+
]
14+
},
15+
"diagnostics": "The identifier system is not valid.",
16+
"severity": "error"
17+
}
1818
],
1919
"resourceType": "OperationOutcome"
2020
}

0 commit comments

Comments
 (0)