From 27884539eb22da380ec869d843f2ae93c2abbdd1 Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Thu, 16 Jul 2026 13:56:08 +0100 Subject: [PATCH 1/9] try explicit false --- azure/azure-release-pipeline.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/azure/azure-release-pipeline.yml b/azure/azure-release-pipeline.yml index e7b10a18e..9687628c9 100644 --- a/azure/azure-release-pipeline.yml +++ b/azure/azure-release-pipeline.yml @@ -37,6 +37,8 @@ extends: prod_producer_approval: true apigee_deployments: - environment: internal-dev + enable_monitoring: false + enable_status_monitoring: false jinja_templates: ENVIRONMENT_TYPE: 'internal' ERROR_ABOUT_LINK: ${{ variables.error_about_link }} @@ -48,6 +50,8 @@ extends: - environment: internal-dev-sandbox proxy_path: sandbox + enable_monitoring: false + enable_status_monitoring: false jinja_templates: ENVIRONMENT_TYPE: 'sandbox' ERROR_ABOUT_LINK: ${{ variables.error_about_link }} @@ -66,6 +70,8 @@ extends: - environment: ref depends_on: - manual_approval_ref + enable_monitoring: false + enable_status_monitoring: false jinja_templates: ENVIRONMENT_TYPE: 'internal' ERROR_ABOUT_LINK: ${{ variables.error_about_link }} @@ -83,6 +89,8 @@ extends: - environment: internal-qa depends_on: - manual_approval_internal_qa + enable_monitoring: false + enable_status_monitoring: false jinja_templates: ENVIRONMENT_TYPE: 'internal' ERROR_ABOUT_LINK: ${{ variables.error_about_link }} @@ -101,6 +109,8 @@ extends: proxy_path: sandbox depends_on: - manual_approval_internal_qa_sandbox + enable_monitoring: false + enable_status_monitoring: false jinja_templates: ENVIRONMENT_TYPE: 'sandbox' ERROR_ABOUT_LINK: ${{ variables.error_about_link }} @@ -120,6 +130,8 @@ extends: depends_on: - manual_approval_sandbox proxy_path: sandbox + enable_monitoring: false + enable_status_monitoring: false jinja_templates: ENVIRONMENT_TYPE: 'sandbox' ERROR_ABOUT_LINK: ${{ variables.error_about_link }} From 23d746a346a75bfbf143cfe01283c709ac96edde Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Fri, 17 Jul 2026 12:16:49 +0100 Subject: [PATCH 2/9] switched client-provided URI placeholders to use curly brackets --- scripts/publish_zap_compatible.py | 4 ++-- specification/communications-manager.yaml | 6 +++--- specification/documentation/APIDescription.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/publish_zap_compatible.py b/scripts/publish_zap_compatible.py index f9bd467da..bd5391bb7 100644 --- a/scripts/publish_zap_compatible.py +++ b/scripts/publish_zap_compatible.py @@ -42,8 +42,8 @@ def scan_and_remove(obj, mappings): ( ("format", "date"), ("personalisation", None), - ("/", None), - ("/", None), + ("/{client-provided-message-status-URI}", None), + ("/{client-provided-channel-status-URI}", None), ) ), f diff --git a/specification/communications-manager.yaml b/specification/communications-manager.yaml index 5c2979ad8..4ab713263 100644 --- a/specification/communications-manager.yaml +++ b/specification/communications-manager.yaml @@ -35,15 +35,15 @@ paths: /channels/nhsapp/accounts: get: $ref: endpoints/get_nhsapp_account_details.yaml - /: + /{client-provided-message-status-URI}: post: $ref: callbacks/message_status.yaml tags: ['Callbacks'] - /: + /{client-provided-channel-status-URI}: post: $ref: callbacks/channel_status.yaml tags: ['Callbacks'] - /: + /{client-provided-recipient-response-URI}: post: $ref: callbacks/recipient_response.yaml tags: ['Callbacks'] diff --git a/specification/documentation/APIDescription.md b/specification/documentation/APIDescription.md index b103572a0..c7b6d0ab7 100644 --- a/specification/documentation/APIDescription.md +++ b/specification/documentation/APIDescription.md @@ -253,7 +253,7 @@ Answer options: [Yes, No] In order to present the recipient with answers, include the `answerOptions` field. -If you subscribe to recipient response callbacks, NHS Notify will send you a callback when a recipient responds to a message (currently only NHS App supports this). See [the recipient response callback](#post-/) for more details. +If you subscribe to recipient response callbacks, NHS Notify will send you a callback when a recipient responds to a message (currently only NHS App supports this). See [the recipient response callback](#post-/{client-provided-recipient-response-URI}) for more details. ## Message character limits Different character limits apply to each of the communication channels as listed below. NHS Notify will validate that any personalisation fields submitted in the send message request do not exceed these limits but it is the client's responsibility to ensure that when personalisation is combined with any templated text, the channel character limit is not exceeded. From db26e43d1465e00cef9fc3cbff9e53b258bc8a6d Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Fri, 17 Jul 2026 16:39:32 +0100 Subject: [PATCH 3/9] kick pipeline From e8e654f82f1741981096c301f3df46ee14db434b Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Mon, 20 Jul 2026 14:06:52 +0100 Subject: [PATCH 4/9] remove tmp files for sonar --- scripts/perform-static-analysis.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/perform-static-analysis.sh b/scripts/perform-static-analysis.sh index 452b37d00..4aaf66ec0 100755 --- a/scripts/perform-static-analysis.sh +++ b/scripts/perform-static-analysis.sh @@ -30,6 +30,8 @@ function main() { function create-report() { + rm -rf sandbox/coverage/tmp + docker run --rm --platform linux/amd64 \ --volume $PWD:/usr/src \ sonarsource/sonar-scanner-cli:$image_version \ From b0ca945adec8b239d8ae8e010df0d45fafe26a6d Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Mon, 20 Jul 2026 14:09:54 +0100 Subject: [PATCH 5/9] replace `{}` in links (Bloomreach adds them) Co-authored-by: Ian Hodges --- specification/documentation/APIDescription.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/documentation/APIDescription.md b/specification/documentation/APIDescription.md index c7b6d0ab7..c4a24db72 100644 --- a/specification/documentation/APIDescription.md +++ b/specification/documentation/APIDescription.md @@ -253,7 +253,7 @@ Answer options: [Yes, No] In order to present the recipient with answers, include the `answerOptions` field. -If you subscribe to recipient response callbacks, NHS Notify will send you a callback when a recipient responds to a message (currently only NHS App supports this). See [the recipient response callback](#post-/{client-provided-recipient-response-URI}) for more details. +If you subscribe to recipient response callbacks, NHS Notify will send you a callback when a recipient responds to a message (currently only NHS App supports this). See [the recipient response callback](#post-/-client-provided-recipient-response-URI-) for more details. ## Message character limits Different character limits apply to each of the communication channels as listed below. NHS Notify will validate that any personalisation fields submitted in the send message request do not exceed these limits but it is the client's responsibility to ensure that when personalisation is combined with any templated text, the channel character limit is not exceeded. From 93f59579713911ff9e158a32e0486cf50b8fb6ac Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Mon, 20 Jul 2026 14:16:56 +0100 Subject: [PATCH 6/9] include recipient-response in ZAP exclusions (as not in our API) --- scripts/publish_zap_compatible.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/publish_zap_compatible.py b/scripts/publish_zap_compatible.py index bd5391bb7..075ab987c 100644 --- a/scripts/publish_zap_compatible.py +++ b/scripts/publish_zap_compatible.py @@ -44,6 +44,7 @@ def scan_and_remove(obj, mappings): ("personalisation", None), ("/{client-provided-message-status-URI}", None), ("/{client-provided-channel-status-URI}", None), + ("/{client-provided-recipient-response-URI}", None), ) ), f From a65f2a95cccdb2fa6796b15c2f3e11e862087ef8 Mon Sep 17 00:00:00 2001 From: Tim Marston Date: Mon, 20 Jul 2026 15:39:44 +0100 Subject: [PATCH 7/9] correction to links in recipient-response schema --- specification/schemas/components/RecipientResponse.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specification/schemas/components/RecipientResponse.yaml b/specification/schemas/components/RecipientResponse.yaml index ea1ea49ed..a8e42cac8 100644 --- a/specification/schemas/components/RecipientResponse.yaml +++ b/specification/schemas/components/RecipientResponse.yaml @@ -69,13 +69,13 @@ properties: links: type: object properties: - recipientResponse: + messageResponses: type: string description: URL to retrieve the recipient response details. format: uri - example: "https://api.service.nhs.uk/comms/v1/messages/2WL3qFTEFM0qMY8xjRbt1LIKCzM/recipient-response/33333333-3333-4333-8333-333333333333" + example: "https://api.service.nhs.uk/comms/v1/message-responses/2WL3qFTEFM0qMY8xjRbt1LIKCzM" required: - - recipientResponse + - messageResponses meta: type: object properties: From 39b02a1e297a0fc0d56a7506d7f1d9408a9aa2dd Mon Sep 17 00:00:00 2001 From: La Penna Luca <160487283+lapenna-bjss@users.noreply.github.com> Date: Thu, 23 Jul 2026 08:22:28 +0100 Subject: [PATCH 8/9] CCM-17830: Double PROD rate limits (#1068) --- .github/workflows/build.yml | 11 +- Makefile | 6 ++ manifest_template.yml | 14 ++- package-lock.json | 159 +++++++++++++++++++++++++++++ package.json | 4 +- scripts/perform-static-analysis.sh | 2 - 6 files changed, 185 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37ba088f4..9774b40d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,15 +28,16 @@ jobs: sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg sudo mkdir -p /etc/apt/keyrings - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg + curl --proto '=https' --tlsv1.2 -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg NODE_MAJOR=20 echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list - name: Upgrade python packaging tools - run: python -m pip install --upgrade pip setuptools wheel + run: python -m pip install pip==26.1.2 setuptools==83.0.0 wheel==0.47.0 - name: Install poetry - run: pip install poetry + run: | + pip install --only-binary :all: poetry==2.2.1 - name: Cache poetry packages uses: actions/cache@v4 @@ -58,9 +59,7 @@ jobs: cd sandbox npm run test cd - - mkdir coverage coverage-temp - TMPDIR="./coverage-temp" npx lcov-result-merger sandbox/coverage/lcov.info coverage/lcov.info --prepend-source-files - rm -r coverage-temp + make merge-coverage-reports - name: Check licenses run: make check-licenses diff --git a/Makefile b/Makefile index 8a6a7c2d1..02c66a0b8 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,12 @@ install-hooks: .git/hooks/pre-commit lint: .check-licenses .ensure-test-documentation-validity .lint-js .lint-python npm run lint +merge-coverage-reports: + mkdir -p coverage coverage-temp + TMPDIR="./coverage-temp" npm run merge-coverage-reports + rm -r coverage-temp + rm -rf sandbox/coverage/tmp + static-analysis: npm run static-analysis diff --git a/manifest_template.yml b/manifest_template.yml index 7bbabb04b..e39a4e325 100644 --- a/manifest_template.yml +++ b/manifest_template.yml @@ -15,6 +15,8 @@ APIGEE_ENVIRONMENTS: has_mock_auth: true variants: - name: internal-qa + global_ratelimit: '18000pm' + global_quota: '6000' display_name: Internal QA - name: ref has_mock_auth: true @@ -29,18 +31,26 @@ APIGEE_ENVIRONMENTS: variants: - name: internal-dev-sandbox display_name: Internal Development Sandbox + global_ratelimit: '18000pm' + global_quota: '6000' - name: internal-qa-sandbox variants: - name: internal-qa-sandbox display_name: Internal QA Sandbox + global_ratelimit: '18000pm' + global_quota: '6000' - name: sandbox variants: - name: sandbox display_name: Sandbox + global_ratelimit: '18000pm' + global_quota: '6000' - name: int variants: - name: int display_name: Integration Testing + global_ratelimit: '18000pm' + global_quota: '6000' - name: prod variants: - name: prod @@ -73,11 +83,11 @@ apigee: quota: enabled: true interval: 1 - limit: {{ VARIANT.global_quota | default(6000)}} + limit: {{ VARIANT.global_quota | default(12000)}} timeunit: minute spikeArrest: enabled: true - ratelimit: {{ VARIANT.global_ratelimit | default('18000pm') }} + ratelimit: {{ VARIANT.global_ratelimit | default('36000pm') }} app: quota: enabled: true diff --git a/package-lock.json b/package-lock.json index c2a1b1e1e..9732fb539 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "eslint-plugin-workspaces": "^0.11.0", "eslint-plugin-yml": "^1.2.0", "handlebars": "^4.7.9", + "lcov-result-merger": "^6.0.0", "license-checker": "^25.0.1", "minimist": "^1.2.2", "newman": "^6.2.2", @@ -4012,6 +4013,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -5389,6 +5403,151 @@ "node": ">=0.10" } }, + "node_modules/lcov-result-merger": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lcov-result-merger/-/lcov-result-merger-6.0.0.tgz", + "integrity": "sha512-RsXi/N4J9b37mu83d3L8WdCGU0jsftN6GlXaJySK6fu+zD48joHC579hpA3K4brPvwWOhbNX5NoT7mghxDi5qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.3", + "yargs": "^18.0.0" + }, + "bin": { + "lcov-result-merger": "bin/lcov-result-merger.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/lcov-result-merger/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/lcov-result-merger/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/lcov-result-merger/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/lcov-result-merger/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/lcov-result-merger/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lcov-result-merger/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/lcov-result-merger/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lcov-result-merger/node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/lcov-result-merger/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", diff --git a/package.json b/package.json index ba5fddaab..b079de7d9 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "sandbox-postman-collection": "newman run postman/NhsNotify.Sandbox.postman_collection.json", "integration-postman-collection": "poetry run python scripts/build_postman_environment.py && newman run postman/NhsNotify.Integration.postman_collection.json -e postman/Integration.test.postman_environment.json", "static-analysis": "./scripts/perform-static-analysis.sh", - "zap-security-scan": "./scripts/run_zap.sh" + "zap-security-scan": "./scripts/run_zap.sh", + "merge-coverage-reports": "lcov-result-merger sandbox/coverage/lcov.info coverage/lcov.info --prepend-source-files" }, "author": "NHS Digital", "license": "MIT", @@ -41,6 +42,7 @@ "eslint-plugin-workspaces": "^0.11.0", "eslint-plugin-yml": "^1.2.0", "handlebars": "^4.7.9", + "lcov-result-merger": "^6.0.0", "license-checker": "^25.0.1", "minimist": "^1.2.2", "newman": "^6.2.2", diff --git a/scripts/perform-static-analysis.sh b/scripts/perform-static-analysis.sh index 4aaf66ec0..452b37d00 100755 --- a/scripts/perform-static-analysis.sh +++ b/scripts/perform-static-analysis.sh @@ -30,8 +30,6 @@ function main() { function create-report() { - rm -rf sandbox/coverage/tmp - docker run --rm --platform linux/amd64 \ --volume $PWD:/usr/src \ sonarsource/sonar-scanner-cli:$image_version \ From 26dd41ca6291c3d7e8e0041487c9019d9e5732d9 Mon Sep 17 00:00:00 2001 From: Angel Pastor Date: Thu, 30 Jul 2026 13:29:38 +0100 Subject: [PATCH 9/9] +minor