Skip to content

Commit 9f28cb9

Browse files
Merge branch 'develop' into RAA-12064-patch
2 parents 019609c + 7c785b7 commit 9f28cb9

14 files changed

Lines changed: 633 additions & 3 deletions

File tree

proxies/sandbox/apiproxy/policies/AssignMessage.AddCors.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Set>
55
<Headers>
66
<Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
7-
<Header name="Access-Control-Allow-Headers">origin, x-requested-with, x-correlation-id, accept, content-type, nhsd-session-urid, nhsd-end-user-organisation-ods, nhsd-ers-business-function, nhsd-ers-on-behalf-of-user-id, authorization, nhsd-ers-comm-rule-org, nhsd-ers-file-name, nhsd-ers-referral-id, if-match</Header>
7+
<Header name="Access-Control-Allow-Headers">origin, x-requested-with, x-correlation-id, accept, content-type, nhsd-session-urid, nhsd-end-user-organisation-ods, nhsd-ers-business-function, nhsd-ers-on-behalf-of-user-id, authorization, nhsd-ers-comm-rule-org, nhsd-ers-file-name, nhsd-ers-file-size, nhsd-ers-file-mime-type, nhsd-ers-referral-id, if-match</Header>
88
<Header name="Access-Control-Expose-Headers">x-correlation-id, x-request-id, content-type, Location, ETag, Content-Disposition, Content-Length, Cache-Control</Header>
99
<Header name="Access-Control-Max-Age">3628800</Header>
1010
<Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE, HEAD</Header>

sandbox/src/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const init = async () => {
3636
routes: {
3737
cors: {
3838
origin: ["*"],
39-
headers: ['origin', 'x-requested-with', 'x-correlation-id', 'accept', 'content-type', 'nhsd-session-urid', 'nhsd-end-user-organisation-ods', 'nhsd-ers-business-function', 'authorization', 'nhsd-ers-comm-rule-org', 'nhsd-ers-file-name', 'nhsd-ers-referral-id', 'if-match', 'nhsd-ers-on-behalf-of-user-id'],
39+
headers: ['origin', 'x-requested-with', 'x-correlation-id', 'accept', 'content-type', 'nhsd-session-urid', 'nhsd-end-user-organisation-ods', 'nhsd-ers-business-function', 'authorization', 'nhsd-ers-comm-rule-org', 'nhsd-ers-file-name', 'nhsd-ers-file-size', 'nhsd-ers-file-mime-type', 'nhsd-ers-referral-id', 'if-match', 'nhsd-ers-on-behalf-of-user-id'],
4040
exposedHeaders: ['x-correlation-id', 'x-request-id', 'content-type', 'Location', 'ETag', 'Content-Disposition', 'Content-Length', 'Cache-Control'],
4141
maxAge: 3628800,
4242
credentials: false
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
A simple test upload file - this is only used by the sandbox integration tests and it's not accessible to the 'try it' UI in Bloomreach
2+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"id": "d497bbe3-f88b-45f1-b3d4-9c563e4c0f5f",
3+
"resourceType": "Binary",
4+
"meta": {
5+
"lastUpdated": "2026-09-04T09:40:04.656Z"
6+
},
7+
"identifier": [
8+
{
9+
"system": "https://fhir.nhs.uk/Id/ers-binary-id",
10+
"value": "d497bbe3-f88b-45f1-b3d4-9c563e4c0f5f"
11+
}
12+
],
13+
"contentType": "text/plain"
14+
}
15+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"resourceType": "OperationOutcome",
3+
"meta": {
4+
"lastUpdated": "2026-09-04T10:15:30.000Z",
5+
"profile": [
6+
"https://www.hl7.org/fhir/R4/operationoutcome.html"
7+
]
8+
},
9+
"issue": [
10+
{
11+
"severity": "error",
12+
"code": "required",
13+
"details": {
14+
"coding": [
15+
{
16+
"system": "https://fhir.nhs.uk/CodeSystem/ers-error-codes",
17+
"code": "MISSING_VALUE"
18+
}
19+
]
20+
},
21+
"diagnostics": "Missing required header: NHSD-eRS-File-Name"
22+
}
23+
]
24+
}

sandbox/src/routes/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const retrieveHealthcareService = require('./r4/retrieveHealthcareService')
4343
const searchForHealthcareServices = require('./r4/searchForHealthcareServices')
4444
const searchServiceRequest = require('./r4/searchServiceRequest')
4545
const retrieveAttachmentR4 = require('./r4/retrieveAttachment')
46+
const uploadFileToDocumentStoreR4 = require('./r4/uploadFileToDocumentStore')
4647

4748
/**
4849
* Services
@@ -89,6 +90,7 @@ const routes = [].concat(
8990
searchServiceRequest,
9091
createAdviceAndGuidance,
9192
retrieveAttachmentR4,
93+
uploadFileToDocumentStoreR4,
9294
objectStore
9395
)
9496

sandbox/src/routes/r4/services/mockResponseProvider.js

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
const OBJECT_STORE_FILE_ID = 'd497bbe3-f88b-45f1-b3d4-9c563e4c0f5f'
2+
const DEFAULT_UPLOAD_FILENAME = 'upload.bin'
3+
4+
function stripTrailingSlash(url) {
5+
return url.replace(/\/+$/, '')
6+
}
7+
8+
function getObjectStoreLocation(request) {
9+
const explicitBaseUrl = request.headers['x-ers-sandbox-baseurl']
10+
const baseUrl = typeof explicitBaseUrl === 'string' && explicitBaseUrl.trim()
11+
? stripTrailingSlash(explicitBaseUrl.trim())
12+
: stripTrailingSlash(request.server.info.uri)
13+
14+
return `${baseUrl}/ObjectStore/${OBJECT_STORE_FILE_ID}`
15+
}
16+
17+
function createContentDisposition(fileNameHeaderValue) {
18+
const inputFileName = String(fileNameHeaderValue || '').replace(/[\r\n]/g, '').trim()
19+
const utf8Filename = inputFileName || DEFAULT_UPLOAD_FILENAME
20+
const asciiFallback = utf8Filename
21+
.replace(/[^\x20-\x7E]/g, '_')
22+
.replace(/["\\]/g, '_')
23+
const safeFallback = asciiFallback || DEFAULT_UPLOAD_FILENAME
24+
const encodedFilename = encodeURIComponent(utf8Filename)
25+
26+
return `attachment; filename="${safeFallback}"; filename*=UTF-8''${encodedFilename}`
27+
}
28+
129
module.exports = {
230

331
getExampleResponseForRetrieveBusinessFunctions: function () {
@@ -43,7 +71,7 @@ module.exports = {
4371
getExampleResponseForSearchServiceRequest: function (request) {
4472
let ubrn;
4573
const identifier = request.query.identifier;
46-
74+
4775
if (identifier.includes('|')) {
4876
ubrn = identifier.split('|')[1]
4977
}
@@ -64,6 +92,23 @@ module.exports = {
6492
return 'r4/searchServiceRequest/responses/ResponseExampleEmpty.json'
6593
}
6694

95+
return null
96+
},
97+
98+
getExampleResponseForUploadFileToDocumentStore: function (request) {
99+
const filename = request.headers['nhsd-ers-file-name']
100+
const fileSize = request.headers['nhsd-ers-file-size']
101+
const fileMimeType = request.headers['nhsd-ers-file-mime-type']
102+
103+
if (filename && fileSize && fileMimeType) {
104+
return {
105+
responsePath: 'r4/uploadFileToDocumentStore/responses/BinaryResource.json',
106+
responseCode: 200,
107+
location: getObjectStoreLocation(request),
108+
contentDisposition: createContentDisposition(filename)
109+
}
110+
}
111+
67112
return null
68113
}
69114
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const mockResponseProvider = require('./services/mockResponseProvider')
2+
const validationUtils = require('../common/validationUtils')
3+
4+
module.exports = [
5+
/**
6+
* Sandbox implementation for uploadFileToDocumentStore A039 (R4) endpoint
7+
*/
8+
{
9+
method: 'POST',
10+
path: '/FHIR/R4/Binary',
11+
handler: (request, h) => {
12+
const allowedBusinessFunctions = ['REFERRING_CLINICIAN', 'REFERRING_CLINICIAN_ADMIN', 'SERVICE_PROVIDER_CLINICIAN', 'SERVICE_PROVIDER_CLINICIAN_ADMIN']
13+
14+
const validationResult = validationUtils.validateBusinessFunction(request, h, allowedBusinessFunctions)
15+
if (validationResult) {
16+
return validationResult
17+
}
18+
19+
const exampleResponse = mockResponseProvider.getExampleResponseForUploadFileToDocumentStore(request)
20+
if (exampleResponse) {
21+
const { responsePath, responseCode, location, contentDisposition } = exampleResponse
22+
return h.file(responsePath, { etagMethod: false })
23+
.code(responseCode)
24+
.type('application/fhir+json')
25+
.header('Location', location)
26+
.header('Content-Disposition', contentDisposition)
27+
}
28+
29+
return h.file('r4/uploadFileToDocumentStore/responses/OperationOutcome-422.json').code(422).type('application/fhir+json')
30+
}
31+
}
32+
]
33+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
allOf:
2+
- $ref: './NHSDigital-OperationOutcome.yaml'
3+
- type: object
4+
properties:
5+
meta:
6+
type: object
7+
required:
8+
- lastUpdated
9+
- profile
10+
properties:
11+
profile:
12+
type: array
13+
minItems: 1
14+
items:
15+
type: string
16+
enum:
17+
- 'https://www.hl7.org/fhir/R4/operationoutcome.html'
18+

0 commit comments

Comments
 (0)