From 0750f0b98e505d2027a14db3a5b44da427366fab Mon Sep 17 00:00:00 2001
From: LeoKHoward <38526889+LeoKHoward@users.noreply.github.com>
Date: Wed, 2 Jul 2025 10:25:03 +0100
Subject: [PATCH 1/6] NPA-5191: Update POST QuestionnaireResponse
DUPLICATE_RELATIONSHIP
---
...ip Service Sandbox.postman_collection.json | 69 ++++++++++++++++++-
sandbox/api/constants.py | 1 +
.../errors/duplicate_relationship_error.yaml | 15 ++++
.../validated-relationships-service-api.yaml | 33 +++++----
4 files changed, 103 insertions(+), 15 deletions(-)
create mode 100644 specification/examples/responses/POST_QuestionnaireResponse/errors/duplicate_relationship_error.yaml
diff --git a/postman/Validate Relationship Service Sandbox.postman_collection.json b/postman/Validate Relationship Service Sandbox.postman_collection.json
index 4a240a9a..4e6acc84 100644
--- a/postman/Validate Relationship Service Sandbox.postman_collection.json
+++ b/postman/Validate Relationship Service Sandbox.postman_collection.json
@@ -578,6 +578,73 @@
},
"response": []
},
+ {
+ "name": "Duplicate relationship",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "const expectedResponseBody = {",
+ " \"issue\": [",
+ " {",
+ " \"code\": \"invalid\",",
+ " \"diagnostics\": \"Proxy role already exists.\",",
+ " \"details\": {",
+ " \"coding\": [",
+ " {",
+ " \"code\": \"DUPLICATE_RELATIONSHIP\",",
+ " \"display\": \"Request must be for a new proxy role.\",",
+ " \"system\": \"https://fhir.nhs.uk/R4/CodeSystem/ValidatedRelationships-ErrorOrWarningCode\",",
+ " \"version\": \"1\"",
+ " }",
+ " ]",
+ " },",
+ " \"severity\": \"error\"",
+ " }",
+ " ],",
+ " \"resourceType\": \"OperationOutcome\"",
+ "}",
+ "",
+ "pm.test(\"Status code is 409\", function () {",
+ " pm.response.to.have.status(409);",
+ "});",
+ "",
+ "pm.test(\"Should have correct response body\", () => {",
+ " var responseJson = pm.response.json();",
+ " pm.expect(responseJson).to.eql(expectedResponseBody);",
+ "});",
+ ""
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"resourceType\": \"QuestionnaireResponse\",\n \"status\": \"completed\",\n \"authored\": \"2024-07-15T09:43:03.280Z\",\n \"source\": {\n \"type\": \"RelatedPerson\",\n \"identifier\": {\n \"system\": \"https://fhir.nhs.uk/Id/nhs-number\",\n \"value\": \"9000000049\"\n }\n },\n \"subject\": {\n \"type\": \"Patient\",\n \"identifier\": {\n \"system\": \"https://fhir.nhs.uk/Id/nhs-number\",\n \"value\": \"9000000006\"\n }\n },\n \"questionnaire\": \"https://api.service.nhs.uk/validated-relationships/FHIR/R4/Questionnaire/01dc6813-3421-4d14-948d-a4888241add1\",\n \"item\": [\n {\n \"linkId\": \"relatedPerson\",\n \"text\": \"Proxy details\",\n \"item\": [\n {\n \"linkId\": \"relatedPerson_identifier\",\n \"text\": \"NHS number\",\n \"answer\": [\n {\n \"valueString\": \"9000000049\"\n }\n ]\n },\n {\n \"linkId\": \"relatedPerson_basisForAccess\",\n \"text\": \"Basis for Access\",\n \"answer\": [\n {\n \"valueCoding\": {\n \"system\": \"https://fhir.hl7.org.uk/CodeSystem/UKCore-AdditionalPersonRelationshipRole\",\n \"code\": \"Personal\",\n \"display\": \"Personal relationship with the patient\"\n }\n }\n ]\n },\n {\n \"linkId\": \"relatedPerson_relationship\",\n \"text\": \"Relationship\",\n \"answer\": [\n {\n \"valueCoding\": {\n \"system\": \"http://terminology.hl7.org/CodeSystem/v3-RoleCode\",\n \"code\": \"SPS\",\n \"display\": \"Spouse\"\n }\n }\n ]\n }\n ]\n },\n {\n \"linkId\": \"patient\",\n \"text\": \"Patient details\",\n \"item\": [\n {\n \"linkId\": \"patient_identifier\",\n \"text\": \"NHS number\",\n \"answer\": [\n {\n \"valueString\": \"9000000006\"\n }\n ]\n },\n {\n \"linkId\": \"patient_name\",\n \"text\": \"Name\",\n \"item\": [\n {\n \"linkId\": \"patient_name_first\",\n \"text\": \"First name\",\n \"answer\": [\n {\n \"valueString\": \"Jill\"\n }\n ]\n },\n {\n \"linkId\": \"patient_name_family\",\n \"text\": \"Last name\",\n \"answer\": [\n {\n \"valueString\": \"Jones\"\n }\n ]\n }\n ]\n },\n {\n \"linkId\": \"patient_birthDate\",\n \"text\": \"Date of birth\",\n \"answer\": [\n {\n \"valueDate\": \"1965-01-01\"\n }\n ]\n }\n ]\n },\n {\n \"linkId\": \"requestedAccess\",\n \"text\": \"Requested access\",\n \"item\": [\n {\n \"linkId\": \"requestedAccess_accessLevel\",\n \"text\": \"Requested access level\",\n \"answer\": [\n {\n \"valueCoding\": {\n \"system\": \"https://fhir.nhs.uk/CodeSystem/Proxy-Placeholder-RequestedAccess\",\n \"code\": \"APPT\",\n \"display\": \"Appointment Booking\"\n }\n },\n {\n \"valueCoding\": {\n \"system\": \"https://fhir.nhs.uk/CodeSystem/Proxy-Placeholder-RequestedAccess\",\n \"code\": \"VACC\",\n \"display\": \"Vaccination Records\"\n }\n }\n ]\n },\n {\n \"linkId\": \"requestedAccess_reasonsForAccess\",\n \"text\": \"Reason for access\",\n \"answer\": [\n {\n \"valueCoding\": {\n \"system\": \"https://fhir.nhs.uk/CodeSystem/Proxy-Placeholder-ReasonForAccess\",\n \"code\": \"PRAC\",\n \"display\": \"Practical Reasons\"\n }\n }\n ]\n }\n ]\n }\n ]\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{api_base_url}}/QuestionnaireResponse",
+ "host": [
+ "{{api_base_url}}"
+ ],
+ "path": [
+ "QuestionnaireResponse"
+ ]
+ }
+ },
+ "response": []
+ },
{
"name": "Adult to adult without ability to consent access request",
"event": [
@@ -10183,4 +10250,4 @@
"type": "string"
}
]
-}
\ No newline at end of file
+}
diff --git a/sandbox/api/constants.py b/sandbox/api/constants.py
index 21094a2c..0e022237 100644
--- a/sandbox/api/constants.py
+++ b/sandbox/api/constants.py
@@ -93,6 +93,7 @@
# POST QuestionnaireResponse
POST_QUESTIONNAIRE_RESPONSE_DIRECTORY = "./api/examples/POST_QuestionnaireResponse/"
POST_QUESTIONNAIRE_RESPONSE__SUCCESS = f"{POST_QUESTIONNAIRE_RESPONSE_DIRECTORY}success.yaml"
+POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR = f"{POST_QUESTIONNAIRE_RESPONSE_DIRECTORY}errors/duplicate_relationship_error.yaml"
# GET QuestionnaireResponse
GET_QUESTIONNAIRE_RESPONSE_DIRECTORY = "./api/examples/GET_QuestionnaireResponse/"
diff --git a/specification/examples/responses/POST_QuestionnaireResponse/errors/duplicate_relationship_error.yaml b/specification/examples/responses/POST_QuestionnaireResponse/errors/duplicate_relationship_error.yaml
new file mode 100644
index 00000000..d97dc7cd
--- /dev/null
+++ b/specification/examples/responses/POST_QuestionnaireResponse/errors/duplicate_relationship_error.yaml
@@ -0,0 +1,15 @@
+PostQuestionnaireResponseDuplicateRelationshipError:
+ summary: Duplicate request for proxy role that already exists
+ description: Error response for a duplicate proxy role
+ value:
+ issue:
+ - code: invalid
+ diagnostics: "Proxy role already exists."
+ details:
+ coding:
+ - code: "DUPLICATE_RELATIONSHIP"
+ display: "Request must be for a new proxy role."
+ system: "https://fhir.nhs.uk/R4/CodeSystem/ValidatedRelationships-ErrorOrWarningCode"
+ version: '1'
+ severity: error
+ resourceType: "OperationOutcome"
diff --git a/specification/validated-relationships-service-api.yaml b/specification/validated-relationships-service-api.yaml
index 3d64ea9b..3bdee8b6 100644
--- a/specification/validated-relationships-service-api.yaml
+++ b/specification/validated-relationships-service-api.yaml
@@ -187,9 +187,11 @@ paths:
## Sandbox test scenarios
- | Scenario | Request | Response |
- | --------------- | ----------------------------------------------- | -------------------------------- |
- | Example request | Example questionnaire response from try it now | HTTP Status 200 Success response |
+ | Scenario | Request | Response |
+ | ---------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
+ | Example request | Example questionnaire response from try it now | HTTP Status 200 Success response |
+ | Duplicate relationship | Request for relationship that already exists, with performer identifier value of 9000000049 | HTTP Status 409 and DUPLICATE_RELATIONSHIP error response |
+
### Sandbox constraints
- Questionnaire Response is not validated.
@@ -244,17 +246,18 @@ paths:
description: |
Errors will be returned for the first error encountered in the request. An error occurred as follows:
- | HTTP status | Error code | Description |
- | ----------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
- | 400 | `MISSING_VALUE` | Missing header or parameter. For details, see the `diagnostics` field. |
- | 400 | `INVALID_VALUE` | Invalid Parameter or Invalid operation. |
- | 401 | `ACCESS_DENIED` | Missing or invalid OAuth 2.0 bearer token in request. |
- | 403 | `FORBIDDEN` | Access denied to resource. |
- | 404 | `INVALIDATED_RESOURCE` | Resource that has been marked as invalid was requested - invalid resources cannot be retrieved |
- | 405 | `METHOD_NOT_ALLOWED` | The method is not allowed. |
- | 408 | `TIMEOUT` | Request timed out. |
- | 415 | `UNSUPPORTED_MEDIA` | Unsupported media type. |
- | 429 | `THROTTLED` | You have exceeded your application's [rate limit](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#rate-limits). |
+ | HTTP status | Error code | Description |
+ | ----------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
+ | 400 | `MISSING_VALUE` | Missing header or parameter. For details, see the `diagnostics` field. |
+ | 400 | `INVALID_VALUE` | Invalid Parameter or Invalid operation. |
+ | 401 | `ACCESS_DENIED` | Missing or invalid OAuth 2.0 bearer token in request. |
+ | 403 | `FORBIDDEN` | Access denied to resource. |
+ | 404 | `INVALIDATED_RESOURCE` | Resource that has been marked as invalid was requested - invalid resources cannot be retrieved |
+ | 405 | `METHOD_NOT_ALLOWED` | The method is not allowed. |
+ | 408 | `TIMEOUT` | Request timed out. |
+ | 409 | `DUPLICATE_RELATIONSHIP` | A proposed proxy role already exists for this proxy/patient relationship. |
+ | 415 | `UNSUPPORTED_MEDIA` | Unsupported media type. |
+ | 429 | `THROTTLED` | You have exceeded your application's [rate limit](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#rate-limits). |
content:
application/fhir+json:
@@ -263,6 +266,8 @@ paths:
examples:
accessDeniedError:
$ref: "./examples/responses/errors/access-denied.yaml#/AccessDeniedError"
+ postQuestionnaireResponseDuplicateRelationshipError:
+ $ref: "./examples/responses/POST_QuestionnaireResponse/errors/duplicate_relationship_error.yaml#/PostQuestionnaireResponseDuplicateRelationshipError"
"5XX":
description: |
Errors will be returned for the first error encountered in the request. An error occurred as follows:
From b1b3636886defe99126fb793bd4abc5090c3a645 Mon Sep 17 00:00:00 2001
From: LeoKHoward <38526889+LeoKHoward@users.noreply.github.com>
Date: Wed, 2 Jul 2025 12:06:36 +0100
Subject: [PATCH 2/6] NPA-5191: Add new test
---
sandbox/api/constants.py | 3 +-
sandbox/api/post_questionnaire_response.py | 27 ++++++++++++++--
.../tests/test_post_questionnaire_response.py | 32 +++++++++++++++----
3 files changed, 52 insertions(+), 10 deletions(-)
diff --git a/sandbox/api/constants.py b/sandbox/api/constants.py
index 0e022237..f206c75d 100644
--- a/sandbox/api/constants.py
+++ b/sandbox/api/constants.py
@@ -93,7 +93,8 @@
# POST QuestionnaireResponse
POST_QUESTIONNAIRE_RESPONSE_DIRECTORY = "./api/examples/POST_QuestionnaireResponse/"
POST_QUESTIONNAIRE_RESPONSE__SUCCESS = f"{POST_QUESTIONNAIRE_RESPONSE_DIRECTORY}success.yaml"
-POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR = f"{POST_QUESTIONNAIRE_RESPONSE_DIRECTORY}errors/duplicate_relationship_error.yaml"
+POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR = \
+ f"{POST_QUESTIONNAIRE_RESPONSE_DIRECTORY}errors/duplicate_relationship_error.yaml"
# GET QuestionnaireResponse
GET_QUESTIONNAIRE_RESPONSE_DIRECTORY = "./api/examples/GET_QuestionnaireResponse/"
diff --git a/sandbox/api/post_questionnaire_response.py b/sandbox/api/post_questionnaire_response.py
index fbd955c4..94e8520e 100644
--- a/sandbox/api/post_questionnaire_response.py
+++ b/sandbox/api/post_questionnaire_response.py
@@ -1,7 +1,13 @@
from logging import INFO, basicConfig, getLogger
from typing import Union
-from .constants import INTERNAL_SERVER_ERROR_EXAMPLE, POST_QUESTIONNAIRE_RESPONSE__SUCCESS
+from flask import request
+
+from .constants import (
+ INTERNAL_SERVER_ERROR_EXAMPLE,
+ POST_QUESTIONNAIRE_RESPONSE__SUCCESS,
+ POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR,
+)
from .utils import generate_response_from_example
basicConfig(level=INFO, format="%(asctime)s - %(message)s")
@@ -15,7 +21,24 @@ def post_questionnaire_response_response() -> Union[dict, tuple]:
Union[dict, tuple]: Response for POST /QuestionnaireResponse
"""
try:
- return generate_response_from_example(POST_QUESTIONNAIRE_RESPONSE__SUCCESS, 200)
+ logger.debug("Received request to POST questionnaire response")
+ # Validate body - beyond the scope of sandbox - assume body is valid for scenario
+ json = request.get_json()
+ source_identifier = json.get("source", {}).get("identifier", {}).get("value")
+ response = None
+
+ # Successful questionnaire response
+ if source_identifier in ["9000000009", "9000000017"]: # Example NHS numbers for success
+ response = generate_response_from_example(POST_QUESTIONNAIRE_RESPONSE__SUCCESS, 200)
+ # Duplicate relationship
+ elif source_identifier == "9000000049":
+ response = generate_response_from_example(POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR, 409)
+ else:
+ # Out of scope errors
+ raise ValueError("Invalid Request")
+
+ return response
+
except Exception:
logger.exception("POST questionnaire response failed")
return generate_response_from_example(INTERNAL_SERVER_ERROR_EXAMPLE, 500)
diff --git a/sandbox/api/tests/test_post_questionnaire_response.py b/sandbox/api/tests/test_post_questionnaire_response.py
index 5bd80ded..f15117c6 100644
--- a/sandbox/api/tests/test_post_questionnaire_response.py
+++ b/sandbox/api/tests/test_post_questionnaire_response.py
@@ -4,36 +4,54 @@
import pytest
from flask import Response
+from sandbox.api.constants import (
+ POST_QUESTIONNAIRE_RESPONSE__SUCCESS,
+ POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR,
+)
+
QUESTIONNAIRE_RESPONSE_API_ENDPOINT = "/FHIR/R4/QuestionnaireResponse"
@pytest.mark.parametrize(
- ("url_path", "response_file_name", "status_code"),
+ ("nhs_num", "response_file_name", "status_code"),
[
(
- QUESTIONNAIRE_RESPONSE_API_ENDPOINT,
- "./api/examples/POST_QuestionnaireResponse/success.yaml",
+ "9000000009",
+ POST_QUESTIONNAIRE_RESPONSE__SUCCESS,
200,
),
+ (
+ "9000000049",
+ POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR,
+ 409,
+ ),
],
)
@patch("sandbox.api.post_questionnaire_response.generate_response_from_example")
def test_post_questionnaire_response(
mock_generate_response_from_example: MagicMock,
- url_path: str,
+ nhs_num: str,
response_file_name: str,
- client: object,
status_code: int,
+ client: object,
) -> None:
- """Test related_persons endpoint with identifier only."""
+ """Test POST QuestionnaireResponse endpoint with different scenarios."""
# Arrange
mock_generate_response_from_example.return_value = mocked_response = Response(
dumps({"data": "mocked"}),
status=status_code,
content_type="application/json",
)
+ json = {
+ "resourceType": "QuestionnaireResponse",
+ "source": {
+ "identifier": {
+ "value": nhs_num
+ }
+ }
+ }
# Act
- response = client.post(url_path, json={"data": "mocked"})
+ response = client.post(QUESTIONNAIRE_RESPONSE_API_ENDPOINT, json=json)
# Assert
mock_generate_response_from_example.assert_called_once_with(response_file_name, status_code)
assert response.status_code == status_code
From 08ac996bed1468f85662087391665863c9a29a6b Mon Sep 17 00:00:00 2001
From: LeoKHoward <38526889+LeoKHoward@users.noreply.github.com>
Date: Wed, 2 Jul 2025 12:20:43 +0100
Subject: [PATCH 3/6] NPA-5191: Formatting fix
---
.github/pull_request_template.md | 6 +++---
sandbox/api/constants.py | 3 ++-
sandbox/api/tests/test_post_questionnaire_response.py | 9 +--------
3 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index cba5a14e..d913736d 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -24,9 +24,9 @@ https://nhsd-jira.digital.nhs.uk/browse/NPA-XXXX
-- [ ]
-- [ ]
-- [ ]
+-
+-
+-
---
diff --git a/sandbox/api/constants.py b/sandbox/api/constants.py
index f206c75d..7467d933 100644
--- a/sandbox/api/constants.py
+++ b/sandbox/api/constants.py
@@ -93,8 +93,9 @@
# POST QuestionnaireResponse
POST_QUESTIONNAIRE_RESPONSE_DIRECTORY = "./api/examples/POST_QuestionnaireResponse/"
POST_QUESTIONNAIRE_RESPONSE__SUCCESS = f"{POST_QUESTIONNAIRE_RESPONSE_DIRECTORY}success.yaml"
-POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR = \
+POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR = (
f"{POST_QUESTIONNAIRE_RESPONSE_DIRECTORY}errors/duplicate_relationship_error.yaml"
+)
# GET QuestionnaireResponse
GET_QUESTIONNAIRE_RESPONSE_DIRECTORY = "./api/examples/GET_QuestionnaireResponse/"
diff --git a/sandbox/api/tests/test_post_questionnaire_response.py b/sandbox/api/tests/test_post_questionnaire_response.py
index f15117c6..a746f1ec 100644
--- a/sandbox/api/tests/test_post_questionnaire_response.py
+++ b/sandbox/api/tests/test_post_questionnaire_response.py
@@ -42,14 +42,7 @@ def test_post_questionnaire_response(
status=status_code,
content_type="application/json",
)
- json = {
- "resourceType": "QuestionnaireResponse",
- "source": {
- "identifier": {
- "value": nhs_num
- }
- }
- }
+ json = {"resourceType": "QuestionnaireResponse", "source": {"identifier": {"value": nhs_num}}}
# Act
response = client.post(QUESTIONNAIRE_RESPONSE_API_ENDPOINT, json=json)
# Assert
From 4b86e60259841c86c55c38b53f88b1cf78a3df12 Mon Sep 17 00:00:00 2001
From: LeoKHoward <38526889+LeoKHoward@users.noreply.github.com>
Date: Wed, 2 Jul 2025 13:47:11 +0100
Subject: [PATCH 4/6] NPA-5191: EB suggestions
---
sandbox/api/post_questionnaire_response.py | 2 +-
.../tests/test_post_questionnaire_response.py | 11 +++
.../duplicate_relationship.yaml | 92 +++++++++++++++++++
.../validated-relationships-service-api.yaml | 6 +-
4 files changed, 109 insertions(+), 2 deletions(-)
create mode 100644 specification/examples/requests/POST_QuestionnaireResponse/duplicate_relationship.yaml
diff --git a/sandbox/api/post_questionnaire_response.py b/sandbox/api/post_questionnaire_response.py
index 94e8520e..fb08d136 100644
--- a/sandbox/api/post_questionnaire_response.py
+++ b/sandbox/api/post_questionnaire_response.py
@@ -28,7 +28,7 @@ def post_questionnaire_response_response() -> Union[dict, tuple]:
response = None
# Successful questionnaire response
- if source_identifier in ["9000000009", "9000000017"]: # Example NHS numbers for success
+ if source_identifier in ["9000000009", "9000000017"]:
response = generate_response_from_example(POST_QUESTIONNAIRE_RESPONSE__SUCCESS, 200)
# Duplicate relationship
elif source_identifier == "9000000049":
diff --git a/sandbox/api/tests/test_post_questionnaire_response.py b/sandbox/api/tests/test_post_questionnaire_response.py
index a746f1ec..48894ae5 100644
--- a/sandbox/api/tests/test_post_questionnaire_response.py
+++ b/sandbox/api/tests/test_post_questionnaire_response.py
@@ -7,6 +7,7 @@
from sandbox.api.constants import (
POST_QUESTIONNAIRE_RESPONSE__SUCCESS,
POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR,
+ INTERNAL_SERVER_ERROR_EXAMPLE
)
QUESTIONNAIRE_RESPONSE_API_ENDPOINT = "/FHIR/R4/QuestionnaireResponse"
@@ -20,11 +21,21 @@
POST_QUESTIONNAIRE_RESPONSE__SUCCESS,
200,
),
+ (
+ "9000000017",
+ POST_QUESTIONNAIRE_RESPONSE__SUCCESS,
+ 200,
+ ),
(
"9000000049",
POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR,
409,
),
+ (
+ "INVALID_NHS_NUMBER",
+ INTERNAL_SERVER_ERROR_EXAMPLE,
+ 500,
+ ),
],
)
@patch("sandbox.api.post_questionnaire_response.generate_response_from_example")
diff --git a/specification/examples/requests/POST_QuestionnaireResponse/duplicate_relationship.yaml b/specification/examples/requests/POST_QuestionnaireResponse/duplicate_relationship.yaml
new file mode 100644
index 00000000..901c9f52
--- /dev/null
+++ b/specification/examples/requests/POST_QuestionnaireResponse/duplicate_relationship.yaml
@@ -0,0 +1,92 @@
+QuestionnaireResponseDuplicateRelationship:
+ summary: Duplicate relationship request
+ description: |
+ Example proxy access request that triggers a 409 DUPLICATE_RELATIONSHIP error due to an existing proxy role with NHS number `9000000049` requesting access to act on behalf of a patient (Jill Jones) with NHS number `9000000006`.
+
+ Significant details to point out:
+
+ - `source.type` should be `RelatedPerson` when a proxy is applying
+ - `source.identifier.value` should be the NHS number of the user completing the form - this should correlate with the Identity token in the request
+ - `subject.type` should be `Patient` since it is the patient that is the subject of the application
+ - `subject.identifier.value` should be the NHS Number of the patient to which the application relates
+ - `patient` demographics are present in the request as a result of being provided by the applicant
+ value:
+ resourceType: "QuestionnaireResponse"
+ status: "completed"
+ authored: "2024-07-15T09:43:03.280Z"
+ source:
+ type: "RelatedPerson"
+ identifier:
+ system: "https://fhir.nhs.uk/Id/nhs-number"
+ value: "9000000049"
+ subject:
+ type: "Patient"
+ identifier:
+ system: "https://fhir.nhs.uk/Id/nhs-number"
+ value: "9000000006"
+ questionnaire: "https://api.service.nhs.uk/validated-relationships/FHIR/R4/Questionnaire/01dc6813-3421-4d14-948d-a4888241add1"
+ item:
+ - linkId: "relatedPerson"
+ text: "Proxy details"
+ item:
+ - linkId: "relatedPerson_identifier"
+ text: "NHS number"
+ answer:
+ - valueString: "9000000049"
+ - linkId: "relatedPerson_basisForAccess"
+ text: "Basis for Access"
+ answer:
+ - valueCoding:
+ system: "https://fhir.hl7.org.uk/CodeSystem/UKCore-AdditionalPersonRelationshipRole"
+ code: "Personal"
+ display: "Personal relationship with the patient"
+ - linkId: "relatedPerson_relationship"
+ text: "Relationship"
+ answer:
+ - valueCoding:
+ system: "http://terminology.hl7.org/CodeSystem/v3-RoleCode"
+ code: "SPS"
+ display: "Spouse"
+ - linkId: "patient"
+ text: "Patient details"
+ item:
+ - linkId: "patient_identifier"
+ text: "NHS number"
+ answer:
+ - valueString: "9000000006"
+ - linkId: "patient_name"
+ text: "Name"
+ item:
+ - linkId: "patient_name_first"
+ text: "First name"
+ answer:
+ - valueString: "Jill"
+ - linkId: "patient_name_family"
+ text: "Last name"
+ answer:
+ - valueString: "Jones"
+ - linkId: "patient_birthDate"
+ text: "Date of birth"
+ answer:
+ - valueDate: "1965-01-01"
+ - linkId: "requestedAccess"
+ text: "Requested access"
+ item:
+ - linkId: "requestedAccess_accessLevel"
+ text: "Requested access level"
+ answer:
+ - valueCoding:
+ system: "https://fhir.nhs.uk/CodeSystem/Proxy-Placeholder-RequestedAccess"
+ code: "APPT"
+ display: "Appointment Booking"
+ - valueCoding:
+ system: "https://fhir.nhs.uk/CodeSystem/Proxy-Placeholder-RequestedAccess"
+ code: "VACC"
+ display: "Vaccination Records"
+ - linkId: "requestedAccess_reasonsForAccess"
+ text: "Reason for access"
+ answer:
+ - valueCoding:
+ system: "https://fhir.nhs.uk/CodeSystem/Proxy-Placeholder-ReasonForAccess"
+ code: "PRAC"
+ display: "Practical Reasons"
diff --git a/specification/validated-relationships-service-api.yaml b/specification/validated-relationships-service-api.yaml
index 3bdee8b6..cb7b66b1 100644
--- a/specification/validated-relationships-service-api.yaml
+++ b/specification/validated-relationships-service-api.yaml
@@ -189,7 +189,7 @@ paths:
| Scenario | Request | Response |
| ---------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
- | Example request | Example questionnaire response from try it now | HTTP Status 200 Success response |
+ | Successful request | Valid request with performer identifier value of 9000000009 or 9000000017 | HTTP Status 200 Success response |
| Duplicate relationship | Request for relationship that already exists, with performer identifier value of 9000000049 | HTTP Status 409 and DUPLICATE_RELATIONSHIP error response |
@@ -220,6 +220,8 @@ paths:
$ref: "./examples/requests/POST_QuestionnaireResponse/adult-to-adult-with-capacity.yaml#/QuestionnaireResponseAdultToAdultWithCapacityRequest"
questionnaireResponseAdultNominatesAdultRequest:
$ref: "./examples/requests/POST_QuestionnaireResponse/adult-nominates-adult.yaml#/QuestionnaireResponseAdultNominatesAdultRequest"
+ questionnaireResponseDuplicateRelationship:
+ $ref: "./examples/requests/POST_QuestionnaireResponse/duplicate_relationship.yaml#/QuestionnaireResponseDuplicateRelationship"
application/fhir+json; charset=utf-8:
schema:
$ref: "#/components/schemas/QuestionnaireResponse"
@@ -232,6 +234,8 @@ paths:
$ref: "./examples/requests/POST_QuestionnaireResponse/adult-to-adult-with-capacity.yaml#/QuestionnaireResponseAdultToAdultWithCapacityRequest"
questionnaireResponseAdultNominatesAdultRequest:
$ref: "./examples/requests/POST_QuestionnaireResponse/adult-nominates-adult.yaml#/QuestionnaireResponseAdultNominatesAdultRequest"
+ questionnaireResponseDuplicateRelationship:
+ $ref: "./examples/requests/POST_QuestionnaireResponse/duplicate_relationship.yaml#/QuestionnaireResponseDuplicateRelationship"
responses:
"200":
description: Request was received successfully for processing
From 242673a7c67e4cd5858f13a384d98aa4181bcb9e Mon Sep 17 00:00:00 2001
From: LeoKHoward <38526889+LeoKHoward@users.noreply.github.com>
Date: Wed, 2 Jul 2025 13:48:46 +0100
Subject: [PATCH 5/6] NPA-5191: Formatting in test
---
sandbox/api/tests/test_post_questionnaire_response.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sandbox/api/tests/test_post_questionnaire_response.py b/sandbox/api/tests/test_post_questionnaire_response.py
index 48894ae5..6cef14be 100644
--- a/sandbox/api/tests/test_post_questionnaire_response.py
+++ b/sandbox/api/tests/test_post_questionnaire_response.py
@@ -7,7 +7,7 @@
from sandbox.api.constants import (
POST_QUESTIONNAIRE_RESPONSE__SUCCESS,
POST_QUESTIONNAIRE_RESPONSE__DUPLICATE_RELATIONSHIP_ERROR,
- INTERNAL_SERVER_ERROR_EXAMPLE
+ INTERNAL_SERVER_ERROR_EXAMPLE,
)
QUESTIONNAIRE_RESPONSE_API_ENDPOINT = "/FHIR/R4/QuestionnaireResponse"
From 93683bbf59f0f5d90c3ad81ffbf669f77a951aab Mon Sep 17 00:00:00 2001
From: LeoKHoward <38526889+LeoKHoward@users.noreply.github.com>
Date: Thu, 3 Jul 2025 10:03:15 +0100
Subject: [PATCH 6/6] NPA-5191: Update Postman URL
---
specification/validated-relationships-service-api.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/specification/validated-relationships-service-api.yaml b/specification/validated-relationships-service-api.yaml
index cb7b66b1..3a23d93e 100644
--- a/specification/validated-relationships-service-api.yaml
+++ b/specification/validated-relationships-service-api.yaml
@@ -141,7 +141,7 @@ info:
* only covers a limited set of scenarios
* is open access, so does not allow you to test authorisation
- [
](https://app.getpostman.com/run-collection/45653607-8a3d3121-ca56-4ac8-9d14-cd375ee158fc?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D45653607-8a3d3121-ca56-4ac8-9d14-cd375ee158fc%26entityType%3Dcollection%26workspaceId%3Db7b0feaf-e9be-4780-8e09-d82c6f42138c)
+ [
](https://god.gw.postman.com/run-collection/46399153-5d8500b1-1bed-4494-9d55-ed8487f2898a?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D46399153-5d8500b1-1bed-4494-9d55-ed8487f2898a%26entityType%3Dcollection%26workspaceId%3Da3cfae72-2c53-419e-8850-13c2b51db16d)
### Integration testing