diff --git a/azure/templates/run-tests.yml b/azure/templates/run-tests.yml index e9ad7b916..23dafaf6d 100644 --- a/azure/templates/run-tests.yml +++ b/azure/templates/run-tests.yml @@ -92,6 +92,7 @@ steps: apigee_username: $(APIGEE_USERNAME) apigee_password: $(APIGEE_PASSWORD) - bash: | + export APIGEE_ACCESS_TOKEN="$(secret.AccessToken)" export PRODUCTION_PRIVATE_KEY="$(Pipeline.Workspace)/secrets/$(PRODUCTION_PRIVATE_KEY)" export PRODUCTION_API_KEY="$(PRODUCTION_API_KEY)" export API_ENVIRONMENT="${{parameters.environment}}" @@ -114,6 +115,7 @@ steps: apigee_username: $(APIGEE_USERNAME) apigee_password: $(APIGEE_PASSWORD) - bash: | + export APIGEE_ACCESS_TOKEN="$(secret.AccessToken)" export INTEGRATION_PRIVATE_KEY="$(Pipeline.Workspace)/secrets/$(INTEGRATION_PRIVATE_KEY)" export INTEGRATION_API_KEY="$(INTEGRATION_API_KEY)" export API_ENVIRONMENT="${{parameters.environment}}" diff --git a/proxies/shared/partials/Partial.Target.FaultRules.xml b/proxies/shared/partials/Partial.Target.FaultRules.xml index 5fbb37976..091bd5af0 100644 --- a/proxies/shared/partials/Partial.Target.FaultRules.xml +++ b/proxies/shared/partials/Partial.Target.FaultRules.xml @@ -70,7 +70,7 @@ RaiseFault.404NotFound - response.status.code = 404 + response.status.code = 404 and data.errors == null RaiseFault.413RequestTooLarge diff --git a/proxies/shared/resources/jsc/EnhanceErrorDetails.js b/proxies/shared/resources/jsc/EnhanceErrorDetails.js index 832d546f6..3293279a0 100644 --- a/proxies/shared/resources/jsc/EnhanceErrorDetails.js +++ b/proxies/shared/resources/jsc/EnhanceErrorDetails.js @@ -1,6 +1,5 @@ const errors = context.getVariable('data.errors'); const messageId = context.getVariable('messageid'); -const statusCode = context.getVariable('response.status.code'); const links = { about: "{{ ERROR_ABOUT_LINK }}" }; @@ -8,17 +7,21 @@ const links = { const enhancedErrors = []; JSON.parse(errors).forEach((error, index) => { - enhancedErrors.push({ + var translatedError = { id: messageId + '.' + index, code: error.code, links: links, - status: String(statusCode), + status: String(error.statusCode), title: error.title, - detail: error.message, - source: { + detail: error.message + }; + if (error.field) { + translatedError.source = { pointer: error.field - } - }); + }; + } + + enhancedErrors.push(translatedError); }); context.setVariable("error.content", JSON.stringify({ errors: enhancedErrors })); diff --git a/sandbox/__test__/batch_send.spec.js b/sandbox/__test__/batch_send.spec.js index a868ab0a2..25b04c6ce 100644 --- a/sandbox/__test__/batch_send.spec.js +++ b/sandbox/__test__/batch_send.spec.js @@ -945,6 +945,7 @@ describe("/api/v1/send", () => { field: `/data/attributes/messages/0/recipient/contactDetails`, message: 'Client is not allowed to provide alternative contact details.', + statusCode: 400, }, ], }) @@ -1012,6 +1013,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/sms", message: "Input failed format check", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1052,6 +1054,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/sms", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1119,6 +1122,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/email", message: "Input failed format check", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1159,6 +1163,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/email", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1228,6 +1233,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/address", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1266,6 +1272,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/address", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1308,6 +1315,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/address", message: "`lines` is missing", title: "Missing value", + statusCode: 400, }, ], }) @@ -1349,7 +1357,8 @@ describe("/api/v1/send", () => { code: 'CM_TOO_FEW_ITEMS', field: "/data/attributes/messages/0/recipient/contactDetails/address", message: "Too few address lines were provided", - title: "Too few items" + title: "Too few items", + statusCode: 400, } ] }) @@ -1391,6 +1400,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/address", message: "Too many address lines were provided", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1432,6 +1442,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/address", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1473,6 +1484,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/address", message: "`postcode` is missing", title: "Missing value", + statusCode: 400, }, ], }) @@ -1514,6 +1526,7 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/address/postcode", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1556,12 +1569,14 @@ describe("/api/v1/send", () => { field: "/data/attributes/messages/0/recipient/contactDetails/email", message: "Input failed format check", title: "Invalid value", + statusCode: 400, }, { code: 'CM_TOO_FEW_ITEMS', field: "/data/attributes/messages/0/recipient/contactDetails/address", message: "Too few address lines were provided", - title: "Too few items" + title: "Too few items", + statusCode: 400, }, ], }) diff --git a/sandbox/__test__/messages.spec.js b/sandbox/__test__/messages.spec.js index bc894cfce..a489569d3 100644 --- a/sandbox/__test__/messages.spec.js +++ b/sandbox/__test__/messages.spec.js @@ -650,6 +650,7 @@ describe("/api/v1/messages", () => { field: `/data/attributes/recipient/contactDetails`, message: 'Client is not allowed to provide alternative contact details.', + statusCode: 400, }, ], }) @@ -709,6 +710,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/sms", message: "Input failed format check", title: "Invalid value", + statusCode: 400, }, ], }) @@ -745,6 +747,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/sms", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -804,6 +807,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/email", message: "Input failed format check", title: "Invalid value", + statusCode: 400, }, ], }) @@ -840,6 +844,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/email", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -901,6 +906,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/address", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -935,6 +941,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/address", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -973,6 +980,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/address", message: "`lines` is missing", title: "Missing value", + statusCode: 400, }, ], }) @@ -1010,7 +1018,8 @@ describe("/api/v1/messages", () => { code: 'CM_TOO_FEW_ITEMS', field: "/data/attributes/recipient/contactDetails/address", message: "Too few address lines were provided", - title: "Too few items" + title: "Too few items", + statusCode: 400, } ] }) @@ -1048,6 +1057,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/address", message: "Too many address lines were provided", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1085,6 +1095,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/address", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1122,6 +1133,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/address", message: "`postcode` is missing", title: "Missing value", + statusCode: 400, }, ], }) @@ -1159,6 +1171,7 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/address/postcode", message: "Invalid", title: "Invalid value", + statusCode: 400, }, ], }) @@ -1197,12 +1210,14 @@ describe("/api/v1/messages", () => { field: "/data/attributes/recipient/contactDetails/email", message: "Input failed format check", title: "Invalid value", + statusCode: 400, }, { code: 'CM_TOO_FEW_ITEMS', field: "/data/attributes/recipient/contactDetails/address", message: "Too few address lines were provided", - title: "Too few items" + title: "Too few items", + statusCode: 400, }, ], }) diff --git a/sandbox/handlers/error_scenarios/override_contact_details.js b/sandbox/handlers/error_scenarios/override_contact_details.js index c9f1e1d5e..5fbf4b742 100644 --- a/sandbox/handlers/error_scenarios/override_contact_details.js +++ b/sandbox/handlers/error_scenarios/override_contact_details.js @@ -27,6 +27,7 @@ function smsValidation(sms, path) { code: 'CM_INVALID_VALUE', field: `${path}/recipient/contactDetails/sms`, message: "Input failed format check", + statusCode: 400, }, `Field 'sms': Input failed format check`, ]); @@ -38,6 +39,7 @@ function smsValidation(sms, path) { code: 'CM_INVALID_VALUE', field: `${path}/recipient/contactDetails/sms`, message: "Invalid", + statusCode: 400, }, `Field 'sms': Invalid`, ]); @@ -56,6 +58,7 @@ function emailValidation(email, path) { code: 'CM_INVALID_VALUE', field: `${path}/recipient/contactDetails/email`, message: "Input failed format check", + statusCode: 400, }, `Field 'email': Input failed format check`, ]); @@ -67,6 +70,7 @@ function emailValidation(email, path) { code: 'CM_INVALID_VALUE', field: `${path}/recipient/contactDetails/email`, message: "Invalid", + statusCode: 400, }, `Field 'email': Invalid`, ]); @@ -86,6 +90,7 @@ function addressValidation(address, path) { code: 'CM_INVALID_VALUE', field: `${path}/recipient/contactDetails/address`, message: "Invalid", + statusCode: 400, }, `Field 'address': Invalid`, ]); @@ -98,6 +103,7 @@ function addressValidation(address, path) { code: 'CM_MISSING_VALUE', field: `${path}/recipient/contactDetails/address`, message: "`lines` is missing", + statusCode: 400, }, `Field 'lines': 'lines' is missing`, ]); @@ -109,6 +115,7 @@ function addressValidation(address, path) { code: 'CM_MISSING_VALUE', field: `${path}/recipient/contactDetails/address`, message: "`lines` is missing", + statusCode: 400, }, `Field 'lines': 'lines' is missing`, ]); @@ -121,6 +128,7 @@ function addressValidation(address, path) { code: 'CM_INVALID_VALUE', field: `${path}/recipient/contactDetails/address`, message: "Invalid", + statusCode: 400, }, `Field 'lines': Invalid`, ]); @@ -133,6 +141,7 @@ function addressValidation(address, path) { title: "Too few items", field: `${path}/recipient/contactDetails/address`, message: "Too few address lines were provided", + statusCode: 400, }, `Field 'lines': Too few address lines were provided`, ]); @@ -144,6 +153,7 @@ function addressValidation(address, path) { code: 'CM_INVALID_VALUE', field: `${path}/recipient/contactDetails/address`, message: "Too many address lines were provided", + statusCode: 400, }, `Field 'lines': Too many address lines were provided`, ]); @@ -156,6 +166,7 @@ function addressValidation(address, path) { code: 'CM_MISSING_VALUE', field: `${path}/recipient/contactDetails/address`, message: "`postcode` is missing", + statusCode: 400, }, `Field 'postcode': 'postcode' is missing`, ]); @@ -168,6 +179,7 @@ function addressValidation(address, path) { code: 'CM_INVALID_VALUE', field: `${path}/recipient/contactDetails/address/postcode`, message: "Invalid", + statusCode: 400, }, `Field 'postcode': Invalid`, ]); @@ -194,6 +206,7 @@ export function getAlternateContactDetailsError( title: "Cannot set contact details", field: `${path}/recipient/contactDetails`, message: "Client is not allowed to provide alternative contact details.", + statusCode: 400, }, ] ]; diff --git a/specification/documentation/APIDescription.md b/specification/documentation/APIDescription.md index 439678756..52b99dd0a 100644 --- a/specification/documentation/APIDescription.md +++ b/specification/documentation/APIDescription.md @@ -136,17 +136,52 @@ You need to get your software approved by us before it can go live with this API To understand how our online digital onboarding process works, see [digital onboarding](https://digital.nhs.uk/developer/guides-and-documentation/digital-onboarding). -## Free-text communications +## Enable users to write and send messages from your software -Free-text communications (as opposed to fixed format communications) are possible via the use of a generic template and making use of the personalisation fields to provide the content of the message. In order to make this more convenient the service provides some globally available routing plans that any client can use. +NHS Notify can deliver NHS App messages, emails and text messages that your users write in your software's free-text inputs. -| Global Routing Plan ID | Channel/Supplier | Read wait time (before failing channel) | Personalisation field name | -|--------------------------------------|------------------|-----------------------------------------|----------------------------| -| 00000000-0000-0000-0000-000000000001 | NHS App | 24 hours | body | -| 00000000-0000-0000-0000-000000000002 | Email | | email_body, email_subject | -| 00000000-0000-0000-0000-000000000003 | SMS | | sms_body | +This is also useful if you: -Please see the Postman collections in the [environments and testing section](#overview--environments-and-testing) for examples. +* do not need to set up reusable templates for multiple recipients +* want to send individual messages + +### Setting up your free-text inputs +You'll need to configure your free-text inputs to populate one of our personalisation fields. Each personalisation field is specific to a message channel and will contain the entire message content that your user will enter in your software. + +You'll then need to include this personalisation field in your request. + +### Making your request to send messages from your software +Use the following routing plan IDs and personalisation fields that match the message channel your user will send their message with. + +| Message channel | Personalisation field | Routing plan ID | +|-----------------|----------------------------|--------------------------------------| +| NHS App message | body | 00000000-0000-0000-0000-000000000001 | +| Email | email_subject, email_body | 00000000-0000-0000-0000-000000000002 | +| Text message | sms_body | 00000000-0000-0000-0000-000000000003 | + +For email, use the personalisation field `email_subject` to allow your user to add the email subject line. + +Complete your request in the same way you [send a single message](#post-/v1/messages) or [send a batch of messages](#post-/v1/message-batches). + +When you make your request, we'll aim to deliver the message within: +* 24 hours for NHS App messages +* 72 hours for emails +* 72 hours for text messages + +Your message will have a 'failed' status if it's not delivered within this time. Find out more about [message, channel and supplier statuses](https://notify.nhs.uk/using-nhs-notify/message-channel-supplier-status). + +You can try out example requests in the sandbox environment in our [Postman collection](#overview--environments-and-testing). + +### Formatting messages written in your software +You can use Markdown to add formatting that your users apply to NHS App messages and emails they write in your software. + +For NHS App messages, follow the Markdown guidance in the 'contentString' section of the schema (opens in a new tab). + +For emails, follow the Markdown guidance for: +* links and URLs (opens in a new tab) +* bullet points, headings, horizontal lines, inset text and numbered steps (opens in a new tab) + +Your users will not be able to use personalisation offered by NHS Notify (opens in a new tab) in messages they write from your software. ## Errors diff --git a/specification/schemas/components/MessageStatus.yaml b/specification/schemas/components/MessageStatus.yaml index 30e38f22a..ae4b39793 100644 --- a/specification/schemas/components/MessageStatus.yaml +++ b/specification/schemas/components/MessageStatus.yaml @@ -21,7 +21,7 @@ properties: example: " " channels: type: array - minItems: 1 + minItems: 0 items: type: object properties: