From 9cd69efeabe0de0a4253dd18836b1cde1b936308 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 20:52:28 +0000 Subject: [PATCH 1/9] Upgrade: [dependabot] - bump filelock from 3.20.1 to 3.20.3 (#2618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.20.1 to 3.20.3.
Release notes

Sourced from filelock's releases.

3.20.3

What's Changed

Full Changelog: https://github.com/tox-dev/filelock/compare/3.20.2...3.20.3

3.20.2

What's Changed

New Contributors

Full Changelog: https://github.com/tox-dev/filelock/compare/3.20.1...3.20.2

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=filelock&package-manager=pip&previous-version=3.20.1&new-version=3.20.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/NHSDigital/eps-prescription-status-update-api/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 958b38a6fb..75cd48c470 100644 --- a/poetry.lock +++ b/poetry.lock @@ -562,14 +562,14 @@ files = [ [[package]] name = "filelock" -version = "3.20.1" +version = "3.20.3" description = "A platform independent file lock." optional = false python-versions = ">=3.10" groups = ["main", "dev"] files = [ - {file = "filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a"}, - {file = "filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c"}, + {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, + {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, ] [[package]] From c47c9a2b4cd7c19282a6420c1b3cf3c9a18afa2f Mon Sep 17 00:00:00 2001 From: tstephen-nhs <231503406+tstephen-nhs@users.noreply.github.com> Date: Thu, 15 Jan 2026 09:12:34 +0000 Subject: [PATCH 2/9] feat(gsul): exclude post-dated updates that have not yet taken effect --- packages/gsul/src/dynamoDBclient.ts | 5 +- packages/gsul/src/getStatusUpdates.ts | 61 +++++++++++++--- packages/gsul/src/schema/response.ts | 3 + packages/gsul/tests/testBuildResult.test.ts | 77 ++++++++++++++++++++- 4 files changed, 131 insertions(+), 15 deletions(-) diff --git a/packages/gsul/src/dynamoDBclient.ts b/packages/gsul/src/dynamoDBclient.ts index 7d78fe291b..f177719922 100644 --- a/packages/gsul/src/dynamoDBclient.ts +++ b/packages/gsul/src/dynamoDBclient.ts @@ -40,7 +40,10 @@ export async function getItemsUpdatesForPrescription( itemId: String(singleUpdate.LineItemID), latestStatus: String(singleUpdate.Status), isTerminalState: String(singleUpdate.TerminalStatus) === "completed", - lastUpdateDateTime: String(singleUpdate.LastModified) + lastUpdateDateTime: String(singleUpdate.LastModified), + ...(singleUpdate.PostDatedLastModifiedSetAt && { + postDatedLastModifiedSetAt: String(singleUpdate.PostDatedLastModifiedSetAt) + }) })) } diff --git a/packages/gsul/src/getStatusUpdates.ts b/packages/gsul/src/getStatusUpdates.ts index b2636ad8c3..634879c88d 100644 --- a/packages/gsul/src/getStatusUpdates.ts +++ b/packages/gsul/src/getStatusUpdates.ts @@ -27,7 +27,7 @@ const lambdaHandler = async (event: requestType): Promise => { // this is an async map so it returns an array of promises const itemResults = event.prescriptions.map(async (prescription) => { const queryResult = await getItemsUpdatesForPrescription(prescription.prescriptionID, prescription.odsCode, logger) - return buildResult(prescription, queryResult) + return filterOutPostDatedUpdates(prescription, queryResult) }) // wait for all the promises to complete @@ -40,21 +40,60 @@ const lambdaHandler = async (event: requestType): Promise => { return response } -export const buildResult = ( +export const filterOutPostDatedUpdates = ( inputPrescription: inputPrescriptionType, - items: Array + items: Array, + currentTime: number = Date.now() // injectable for testing ): outputPrescriptionType => { - // get unique item ids with the latest update based on lastUpdateDateTime - const uniqueItems: Array = Object.values( - items.reduce(function (r, e) { - if (!r[e.itemId] || Date.parse(e.lastUpdateDateTime) > Date.parse(r[e.itemId].lastUpdateDateTime)) r[e.itemId] = e - return r - }, {}) - ) + + // filter out items with future lastUpdateDateTime + const validTimeUpdates = items.filter(item => { + const updateTime = Date.parse(item.lastUpdateDateTime) + return updateTime <= currentTime + }) + + // group by itemId and separate post-dated from regular updates + const itemGroups: Record = {} + + validTimeUpdates.forEach(item => { + if (!itemGroups[item.itemId]) { + itemGroups[item.itemId] = {regular: null, postDated: null} + } + const group = itemGroups[item.itemId] + + if (item.postDatedLastModifiedSetAt) { // this is a post-dated update + if (!group.postDated) { + group.postDated = item + } else { + const existingTime = Date.parse(group.postDated.postDatedLastModifiedSetAt!) + const newTime = Date.parse(item.postDatedLastModifiedSetAt) + if (newTime > existingTime) { + group.postDated = item + } + } + } else { // this is a regular update + if (!group.regular) { + group.regular = item + } else { + const existingTime = Date.parse(group.regular.lastUpdateDateTime) + const newTime = Date.parse(item.lastUpdateDateTime) + if (newTime > existingTime) { + group.regular = item + } + } + } + }) + + // flatten both regular and post-dated updates into single array + const uniqueItems: Array = [] + Object.values(itemGroups).forEach(group => { + if (group.regular) uniqueItems.push(group.regular) + if (group.postDated) uniqueItems.push(group.postDated) + }) const result: outputPrescriptionType = { prescriptionID: inputPrescription.prescriptionID, - onboarded: items.length > 0, + onboarded: items.length > 0, // consider onboarded even if all updates were post-dated items: uniqueItems } return result diff --git a/packages/gsul/src/schema/response.ts b/packages/gsul/src/schema/response.ts index cd496e762f..5aeafb4252 100644 --- a/packages/gsul/src/schema/response.ts +++ b/packages/gsul/src/schema/response.ts @@ -15,6 +15,9 @@ const itemSchema = { }, lastUpdateDateTime: { type: "string" + }, + postDatedLastModifiedSetAt: { + type: "string" } } } as const diff --git a/packages/gsul/tests/testBuildResult.test.ts b/packages/gsul/tests/testBuildResult.test.ts index 0847bef88c..a79959d4ea 100644 --- a/packages/gsul/tests/testBuildResult.test.ts +++ b/packages/gsul/tests/testBuildResult.test.ts @@ -1,4 +1,4 @@ -import {buildResult} from "../src/getStatusUpdates" +import {filterOutPostDatedUpdates} from "../src/getStatusUpdates" import {inputPrescriptionType} from "../src/schema/request" import {outputPrescriptionType, itemType} from "../src/schema/response" @@ -83,7 +83,7 @@ const scenarios: Array = [ } }, { - scenarioDescription: "should return correct data for multiple items", + scenarioDescription: "should return latest item for multiple updates for each of multiple statuses", inputPrescriptions: { prescriptionID: "abc", odsCode: "123" @@ -132,11 +132,82 @@ const scenarios: Array = [ } ] } + }, + { + scenarioDescription: "should exclude item when post-dated update hasn't matured", + inputPrescriptions: { + prescriptionID: "abc", + odsCode: "123" + }, + queryResults: [ + { + itemId: "item_1", + latestStatus: "Ready to collect", + isTerminalState: false, + lastUpdateDateTime: "2030-01-01T00:00:00Z", // Future, no fallback + postDatedLastModifiedSetAt:"1972-01-01T00:00:00Z" + } + ], + expectedResult: { + prescriptionID: "abc", + onboarded: true, + items: [] + } + }, + { + scenarioDescription: "should use latest post-dated update when multiple have matured", + inputPrescriptions: { + prescriptionID: "abc", + odsCode: "123" + }, + queryResults: [ + { + itemId: "item_1", + latestStatus: "With pharmacy", + isTerminalState: false, + lastUpdateDateTime: "1970-01-01T00:00:00Z" + }, + { + itemId: "item_1", + latestStatus: "Ready to collect", + isTerminalState: false, + lastUpdateDateTime: "1971-01-01T00:00:00Z", + postDatedLastModifiedSetAt: "1970-01-02T00:00:00Z" + }, + { + itemId: "item_1", + latestStatus: "Ready to collect", + isTerminalState: false, + lastUpdateDateTime: "1972-01-01T00:00:00Z", + postDatedLastModifiedSetAt: "1971-01-02T00:00:00Z" + } + ], + expectedResult: { + prescriptionID: "abc", + onboarded: true, + items: [ + { + itemId: "item_1", + latestStatus: "With pharmacy", + isTerminalState: false, + lastUpdateDateTime: "1970-01-01T00:00:00Z" + }, + { + itemId: "item_1", + latestStatus: "Ready to collect", + isTerminalState: false, + lastUpdateDateTime: "1972-01-01T00:00:00Z", + postDatedLastModifiedSetAt: "1971-01-02T00:00:00Z" + } + ] + } } ] describe("Unit tests for buildResults", () => { it.each(scenarios)("$scenarioDescription", ({inputPrescriptions, queryResults, expectedResult}) => { - const result = buildResult(inputPrescriptions, queryResults) + // Use a fixed time of 2000-01-01 for tests (946684800000 ms since epoch) + const fixedCurrentTime = new Date("2000-01-01T00:00:00Z").getTime() + const result = filterOutPostDatedUpdates(inputPrescriptions, queryResults, fixedCurrentTime) expect(result).toMatchObject(expectedResult) }) }) From 02983ffc5c680064d053c254d4f804f716c95497 Mon Sep 17 00:00:00 2001 From: tstephen-nhs <231503406+tstephen-nhs@users.noreply.github.com> Date: Thu, 15 Jan 2026 11:42:22 +0000 Subject: [PATCH 3/9] fix(gsul): better func name --- packages/gsul/src/getStatusUpdates.ts | 4 ++-- packages/gsul/tests/testBuildResult.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/gsul/src/getStatusUpdates.ts b/packages/gsul/src/getStatusUpdates.ts index 634879c88d..acbe2524ca 100644 --- a/packages/gsul/src/getStatusUpdates.ts +++ b/packages/gsul/src/getStatusUpdates.ts @@ -27,7 +27,7 @@ const lambdaHandler = async (event: requestType): Promise => { // this is an async map so it returns an array of promises const itemResults = event.prescriptions.map(async (prescription) => { const queryResult = await getItemsUpdatesForPrescription(prescription.prescriptionID, prescription.odsCode, logger) - return filterOutPostDatedUpdates(prescription, queryResult) + return filterOutFutureReduceToLatestUpdates(prescription, queryResult) }) // wait for all the promises to complete @@ -40,7 +40,7 @@ const lambdaHandler = async (event: requestType): Promise => { return response } -export const filterOutPostDatedUpdates = ( +export const filterOutFutureReduceToLatestUpdates = ( inputPrescription: inputPrescriptionType, items: Array, currentTime: number = Date.now() // injectable for testing diff --git a/packages/gsul/tests/testBuildResult.test.ts b/packages/gsul/tests/testBuildResult.test.ts index a79959d4ea..6044273d1f 100644 --- a/packages/gsul/tests/testBuildResult.test.ts +++ b/packages/gsul/tests/testBuildResult.test.ts @@ -1,4 +1,4 @@ -import {filterOutPostDatedUpdates} from "../src/getStatusUpdates" +import {filterOutFutureReduceToLatestUpdates} from "../src/getStatusUpdates" import {inputPrescriptionType} from "../src/schema/request" import {outputPrescriptionType, itemType} from "../src/schema/response" @@ -207,7 +207,7 @@ describe("Unit tests for buildResults", () => { it.each(scenarios)("$scenarioDescription", ({inputPrescriptions, queryResults, expectedResult}) => { // Use a fixed time of 2000-01-01 for tests (946684800000 ms since epoch) const fixedCurrentTime = new Date("2000-01-01T00:00:00Z").getTime() - const result = filterOutPostDatedUpdates(inputPrescriptions, queryResults, fixedCurrentTime) + const result = filterOutFutureReduceToLatestUpdates(inputPrescriptions, queryResults, fixedCurrentTime) expect(result).toMatchObject(expectedResult) }) }) From dec2d1205cf56d29413854528bad1f616ff3ffd5 Mon Sep 17 00:00:00 2001 From: anthony-nhs <121869075+anthony-nhs@users.noreply.github.com> Date: Wed, 14 Jan 2026 12:42:12 +0000 Subject: [PATCH 4/9] Chore: [AEA-6107] - upgrade to node 24 (#2590) ## Summary - Routine Change ### Details - upgrade to node 24 --- .tool-versions | 2 +- .vscode/eps-prescription-status-update-api.code-workspace | 4 ++++ SAMtemplates/functions/main.yaml | 2 +- package-lock.json | 4 ++-- packages/capabilityStatement/jest.config.ts | 2 +- packages/checkPrescriptionStatusUpdates/jest.config.ts | 2 +- packages/common/middyErrorHandler/jest.config.ts | 2 +- packages/common/utilities/jest.config.ts | 2 +- packages/cpsuLambda/jest.config.ts | 2 +- packages/gsul/jest.config.ts | 2 +- packages/nhsNotifyLambda/jest.config.ts | 2 +- packages/nhsNotifyUpdateCallback/jest.config.ts | 2 +- packages/nhsd-psu-sandbox/jest.config.ts | 2 +- packages/psuRestoreValidationLambda/jest.config.ts | 2 +- packages/specification/jest.config.ts | 2 +- packages/specification/package.json | 4 ++-- packages/statusLambda/jest.config.ts | 2 +- packages/updatePrescriptionStatus/jest.config.ts | 2 +- 18 files changed, 23 insertions(+), 19 deletions(-) diff --git a/.tool-versions b/.tool-versions index 993d74a521..d958cc3f3b 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,4 +1,4 @@ -nodejs 20.19.5 +nodejs 24.12.0 python 3.12.12 poetry 1.8.2 shellcheck 0.11.0 diff --git a/.vscode/eps-prescription-status-update-api.code-workspace b/.vscode/eps-prescription-status-update-api.code-workspace index 659926e2eb..0653f825d2 100644 --- a/.vscode/eps-prescription-status-update-api.code-workspace +++ b/.vscode/eps-prescription-status-update-api.code-workspace @@ -56,6 +56,10 @@ "name": "packages/common/middyErrorHandler", "path": "../packages/common/middyErrorHandler" }, + { + "name": "packages/common/utilities", + "path": "../packages/common/utilities" + }, { "name": "packages/psuRestoreValidationLambda", "path": "../packages/psuRestoreValidationLambda" diff --git a/SAMtemplates/functions/main.yaml b/SAMtemplates/functions/main.yaml index 89476fe047..9ebfb31b6b 100644 --- a/SAMtemplates/functions/main.yaml +++ b/SAMtemplates/functions/main.yaml @@ -9,7 +9,7 @@ Globals: MemorySize: 256 Architectures: - x86_64 - Runtime: nodejs20.x + Runtime: nodejs24.x Environment: Variables: NODE_OPTIONS: "--enable-source-maps" diff --git a/package-lock.json b/package-lock.json index cc2bad6b54..6aece2d988 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11324,8 +11324,8 @@ "@redocly/cli": "^2.14.4" }, "engines": { - "node": "20.10.x", - "npm": "10.2.x" + "node": "24.12.x", + "npm": "11.6.x" } }, "packages/statusLambda": { diff --git a/packages/capabilityStatement/jest.config.ts b/packages/capabilityStatement/jest.config.ts index 22dd407f31..acbc1493de 100644 --- a/packages/capabilityStatement/jest.config.ts +++ b/packages/capabilityStatement/jest.config.ts @@ -1,4 +1,4 @@ -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" import type {JestConfigWithTsJest} from "ts-jest" const jestConfig: JestConfigWithTsJest = { diff --git a/packages/checkPrescriptionStatusUpdates/jest.config.ts b/packages/checkPrescriptionStatusUpdates/jest.config.ts index d839b751c9..1ad45dd190 100644 --- a/packages/checkPrescriptionStatusUpdates/jest.config.ts +++ b/packages/checkPrescriptionStatusUpdates/jest.config.ts @@ -1,4 +1,4 @@ -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" import type {JestConfigWithTsJest} from "ts-jest" const jestConfig: JestConfigWithTsJest = { diff --git a/packages/common/middyErrorHandler/jest.config.ts b/packages/common/middyErrorHandler/jest.config.ts index 63ae0ac6ac..022989c681 100644 --- a/packages/common/middyErrorHandler/jest.config.ts +++ b/packages/common/middyErrorHandler/jest.config.ts @@ -1,4 +1,4 @@ -import defaultConfig from "../../../jest.default.config" +import defaultConfig from "../../../jest.default.config.ts" import type {JestConfigWithTsJest} from "ts-jest" const jestConfig: JestConfigWithTsJest = { diff --git a/packages/common/utilities/jest.config.ts b/packages/common/utilities/jest.config.ts index 63644906c6..6a6e039c9a 100644 --- a/packages/common/utilities/jest.config.ts +++ b/packages/common/utilities/jest.config.ts @@ -1,5 +1,5 @@ import type {JestConfigWithTsJest} from "ts-jest" -import defaultConfig from "../../../jest.default.config" +import defaultConfig from "../../../jest.default.config.ts" const jestConfig: JestConfigWithTsJest = { ...defaultConfig, diff --git a/packages/cpsuLambda/jest.config.ts b/packages/cpsuLambda/jest.config.ts index fb4d05f26c..d93d212b98 100644 --- a/packages/cpsuLambda/jest.config.ts +++ b/packages/cpsuLambda/jest.config.ts @@ -1,5 +1,5 @@ import type {JestConfigWithTsJest} from "ts-jest" -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" const jestConfig: JestConfigWithTsJest = { ...defaultConfig, diff --git a/packages/gsul/jest.config.ts b/packages/gsul/jest.config.ts index fb4d05f26c..d93d212b98 100644 --- a/packages/gsul/jest.config.ts +++ b/packages/gsul/jest.config.ts @@ -1,5 +1,5 @@ import type {JestConfigWithTsJest} from "ts-jest" -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" const jestConfig: JestConfigWithTsJest = { ...defaultConfig, diff --git a/packages/nhsNotifyLambda/jest.config.ts b/packages/nhsNotifyLambda/jest.config.ts index 32720a510e..8212011ed9 100644 --- a/packages/nhsNotifyLambda/jest.config.ts +++ b/packages/nhsNotifyLambda/jest.config.ts @@ -1,5 +1,5 @@ import type {JestConfigWithTsJest} from "ts-jest" -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" const jestConfig: JestConfigWithTsJest = { ...defaultConfig, diff --git a/packages/nhsNotifyUpdateCallback/jest.config.ts b/packages/nhsNotifyUpdateCallback/jest.config.ts index d839b751c9..1ad45dd190 100644 --- a/packages/nhsNotifyUpdateCallback/jest.config.ts +++ b/packages/nhsNotifyUpdateCallback/jest.config.ts @@ -1,4 +1,4 @@ -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" import type {JestConfigWithTsJest} from "ts-jest" const jestConfig: JestConfigWithTsJest = { diff --git a/packages/nhsd-psu-sandbox/jest.config.ts b/packages/nhsd-psu-sandbox/jest.config.ts index 1ddbc83f6c..dd2cb2f161 100644 --- a/packages/nhsd-psu-sandbox/jest.config.ts +++ b/packages/nhsd-psu-sandbox/jest.config.ts @@ -1,4 +1,4 @@ -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" import type {JestConfigWithTsJest} from "ts-jest" const jestConfig: JestConfigWithTsJest = { diff --git a/packages/psuRestoreValidationLambda/jest.config.ts b/packages/psuRestoreValidationLambda/jest.config.ts index 22dd407f31..acbc1493de 100644 --- a/packages/psuRestoreValidationLambda/jest.config.ts +++ b/packages/psuRestoreValidationLambda/jest.config.ts @@ -1,4 +1,4 @@ -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" import type {JestConfigWithTsJest} from "ts-jest" const jestConfig: JestConfigWithTsJest = { diff --git a/packages/specification/jest.config.ts b/packages/specification/jest.config.ts index 1ddbc83f6c..dd2cb2f161 100644 --- a/packages/specification/jest.config.ts +++ b/packages/specification/jest.config.ts @@ -1,4 +1,4 @@ -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" import type {JestConfigWithTsJest} from "ts-jest" const jestConfig: JestConfigWithTsJest = { diff --git a/packages/specification/package.json b/packages/specification/package.json index 7db14c653f..ec5bd73112 100644 --- a/packages/specification/package.json +++ b/packages/specification/package.json @@ -13,8 +13,8 @@ "author": "NHS Digital", "license": "MIT", "engines": { - "node": "20.10.x", - "npm": "10.2.x" + "node": "24.12.x", + "npm": "11.6.x" }, "homepage": "https://github.com/NHSDigital/eps-prescription-status-update-api", "devDependencies": { diff --git a/packages/statusLambda/jest.config.ts b/packages/statusLambda/jest.config.ts index 1ddbc83f6c..dd2cb2f161 100644 --- a/packages/statusLambda/jest.config.ts +++ b/packages/statusLambda/jest.config.ts @@ -1,4 +1,4 @@ -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" import type {JestConfigWithTsJest} from "ts-jest" const jestConfig: JestConfigWithTsJest = { diff --git a/packages/updatePrescriptionStatus/jest.config.ts b/packages/updatePrescriptionStatus/jest.config.ts index 50432d9f76..fd94bd6a89 100644 --- a/packages/updatePrescriptionStatus/jest.config.ts +++ b/packages/updatePrescriptionStatus/jest.config.ts @@ -1,5 +1,5 @@ import type {JestConfigWithTsJest} from "ts-jest" -import defaultConfig from "../../jest.default.config" +import defaultConfig from "../../jest.default.config.ts" const jestConfig: JestConfigWithTsJest = { ...defaultConfig, From 589db1ed506f0302922d5169d56dcc31a0df5155 Mon Sep 17 00:00:00 2001 From: tstephen-nhs <231503406+tstephen-nhs@users.noreply.github.com> Date: Thu, 15 Jan 2026 12:27:20 +0000 Subject: [PATCH 5/9] chore: add SQ plugin for 'clean as you code' --- .devcontainer/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ee5ab9687f..690609e904 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -36,7 +36,8 @@ "streetsidesoftware.code-spell-checker", "timonwong.shellcheck", "mkhl.direnv", - "github.vscode-github-actions" + "github.vscode-github-actions", + "sonarsource.sonarlint-vscode" ], "settings": { "python.defaultInterpreterPath": "/workspaces/eps-prescription-status-update-api/.venv/bin/python", From 7b3fa85adfb9cb54038fae966a91a78be2bc238e Mon Sep 17 00:00:00 2001 From: tstephen-nhs <231503406+tstephen-nhs@users.noreply.github.com> Date: Thu, 15 Jan 2026 12:31:52 +0000 Subject: [PATCH 6/9] chore: simplify if stmt / placate SQ --- packages/gsul/src/getStatusUpdates.ts | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/packages/gsul/src/getStatusUpdates.ts b/packages/gsul/src/getStatusUpdates.ts index acbe2524ca..1da196d4e7 100644 --- a/packages/gsul/src/getStatusUpdates.ts +++ b/packages/gsul/src/getStatusUpdates.ts @@ -62,25 +62,15 @@ export const filterOutFutureReduceToLatestUpdates = ( const group = itemGroups[item.itemId] if (item.postDatedLastModifiedSetAt) { // this is a post-dated update - if (!group.postDated) { - group.postDated = item - } else { - const existingTime = Date.parse(group.postDated.postDatedLastModifiedSetAt!) - const newTime = Date.parse(item.postDatedLastModifiedSetAt) - if (newTime > existingTime) { - group.postDated = item - } - } + const existingTime = Date.parse(group.postDated.postDatedLastModifiedSetAt) + const newTime = Date.parse(item.postDatedLastModifiedSetAt) + group.postDated = !group.postDated || newTime > existingTime + ? item + : group.postDated } else { // this is a regular update - if (!group.regular) { - group.regular = item - } else { - const existingTime = Date.parse(group.regular.lastUpdateDateTime) - const newTime = Date.parse(item.lastUpdateDateTime) - if (newTime > existingTime) { - group.regular = item - } - } + const existingTime = Date.parse(group.regular.lastUpdateDateTime) + const newTime = Date.parse(item.lastUpdateDateTime) + group.regular = !group.regular || newTime > existingTime ? item : group.regular } }) From 0b3fc9105ebfc0fc4c0b3d4b7083ee614664efce Mon Sep 17 00:00:00 2001 From: tstephen-nhs <231503406+tstephen-nhs@users.noreply.github.com> Date: Thu, 15 Jan 2026 12:48:32 +0000 Subject: [PATCH 7/9] chore: simplify if stmt / placate SQ --- packages/gsul/src/getStatusUpdates.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/gsul/src/getStatusUpdates.ts b/packages/gsul/src/getStatusUpdates.ts index 1da196d4e7..abada87188 100644 --- a/packages/gsul/src/getStatusUpdates.ts +++ b/packages/gsul/src/getStatusUpdates.ts @@ -61,16 +61,22 @@ export const filterOutFutureReduceToLatestUpdates = ( } const group = itemGroups[item.itemId] - if (item.postDatedLastModifiedSetAt) { // this is a post-dated update + if (item.postDatedLastModifiedSetAt && !group.postDated) { // this is a post-dated update + group.postDated = item + } else if (item.postDatedLastModifiedSetAt && group.postDated) { // also a post-dated update const existingTime = Date.parse(group.postDated.postDatedLastModifiedSetAt) const newTime = Date.parse(item.postDatedLastModifiedSetAt) - group.postDated = !group.postDated || newTime > existingTime - ? item - : group.postDated - } else { // this is a regular update + if (newTime > existingTime) { + group.postDated = item + } + } else if (!group.regular) { // this is a regular update + group.regular = item + } else if (group.regular) { // also a regular update const existingTime = Date.parse(group.regular.lastUpdateDateTime) const newTime = Date.parse(item.lastUpdateDateTime) - group.regular = !group.regular || newTime > existingTime ? item : group.regular + if (newTime > existingTime) { + group.regular = item + } } }) From ca5cfdbd4c6661f98fa9b915b44c707ab07f4c55 Mon Sep 17 00:00:00 2001 From: tstephen-nhs <231503406+tstephen-nhs@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:59:46 +0000 Subject: [PATCH 8/9] fix: precautionary tolower on comparison --- packages/gsul/src/dynamoDBclient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gsul/src/dynamoDBclient.ts b/packages/gsul/src/dynamoDBclient.ts index f177719922..1aa871bd39 100644 --- a/packages/gsul/src/dynamoDBclient.ts +++ b/packages/gsul/src/dynamoDBclient.ts @@ -39,7 +39,7 @@ export async function getItemsUpdatesForPrescription( return items.map((singleUpdate) => ({ itemId: String(singleUpdate.LineItemID), latestStatus: String(singleUpdate.Status), - isTerminalState: String(singleUpdate.TerminalStatus) === "completed", + isTerminalState: String(singleUpdate.TerminalStatus).toLowerCase() === "completed", lastUpdateDateTime: String(singleUpdate.LastModified), ...(singleUpdate.PostDatedLastModifiedSetAt && { postDatedLastModifiedSetAt: String(singleUpdate.PostDatedLastModifiedSetAt) From 69875821083be29f8225ae2a49e60d336ccf2f66 Mon Sep 17 00:00:00 2001 From: tstephen-nhs <231503406+tstephen-nhs@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:01:02 +0000 Subject: [PATCH 9/9] chore: additional test cases identified --- packages/gsul/tests/testBuildResult.test.ts | 81 ++++++++++++++++++--- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/packages/gsul/tests/testBuildResult.test.ts b/packages/gsul/tests/testBuildResult.test.ts index 6044273d1f..918b817bf9 100644 --- a/packages/gsul/tests/testBuildResult.test.ts +++ b/packages/gsul/tests/testBuildResult.test.ts @@ -8,6 +8,8 @@ type scenariosType = { queryResults: Array expectedResult: outputPrescriptionType } +const now = new Date() +const futureDateTime = new Date(now.valueOf() + (24 * 60 * 60 * 1000)).toISOString() const scenarios: Array = [ { scenarioDescription: "should return correct data when a matched prescription found", @@ -97,9 +99,9 @@ const scenarios: Array = [ }, { itemId: "item_1", - latestStatus: "latest_item_1_status", + latestStatus: "item_1_status", isTerminalState: true, - lastUpdateDateTime: "1972-01-01T00:00:00Z" + lastUpdateDateTime: "1972-01-01T00:00:00Z" // newer update for item_1 }, { itemId: "item_2", @@ -109,9 +111,9 @@ const scenarios: Array = [ }, { itemId: "item_2", - latestStatus: "early_item_2_status", + latestStatus: "item_2_status", isTerminalState: true, - lastUpdateDateTime: "1970-01-01T00:00:00Z" + lastUpdateDateTime: "1970-01-01T00:00:00Z" // older update for item_2 } ], expectedResult: { @@ -120,7 +122,7 @@ const scenarios: Array = [ items: [ { itemId: "item_1", - latestStatus: "latest_item_1_status", + latestStatus: "item_1_status", isTerminalState: true, lastUpdateDateTime: "1972-01-01T00:00:00Z" }, @@ -171,15 +173,28 @@ const scenarios: Array = [ itemId: "item_1", latestStatus: "Ready to collect", isTerminalState: false, - lastUpdateDateTime: "1971-01-01T00:00:00Z", - postDatedLastModifiedSetAt: "1970-01-02T00:00:00Z" + lastUpdateDateTime: "1970-01-02T00:00:00Z", + postDatedLastModifiedSetAt: "1970-01-01T00:00:00Z" // first RTC: post-dated and matured + }, + { + itemId: "item_1", + latestStatus: "Ready to collect", + isTerminalState: false, + lastUpdateDateTime: futureDateTime, + postDatedLastModifiedSetAt: "1970-01-02T00:00:00Z" // second RTC: post-dated and yet to mature + }, + { + itemId: "item_1", + latestStatus: "With pharmacy", + isTerminalState: false, + lastUpdateDateTime: "1970-01-03T00:00:00Z" // Back to 'With pharmacy' }, { itemId: "item_1", latestStatus: "Ready to collect", isTerminalState: false, - lastUpdateDateTime: "1972-01-01T00:00:00Z", - postDatedLastModifiedSetAt: "1971-01-02T00:00:00Z" + lastUpdateDateTime: "1970-01-04T00:00:00Z", + postDatedLastModifiedSetAt: "1970-01-03T00:00:00Z" // third RTC: post-dated and matured } ], expectedResult: { @@ -190,17 +205,59 @@ const scenarios: Array = [ itemId: "item_1", latestStatus: "With pharmacy", isTerminalState: false, - lastUpdateDateTime: "1970-01-01T00:00:00Z" + lastUpdateDateTime: "1970-01-03T00:00:00Z" }, { itemId: "item_1", latestStatus: "Ready to collect", isTerminalState: false, - lastUpdateDateTime: "1972-01-01T00:00:00Z", - postDatedLastModifiedSetAt: "1971-01-02T00:00:00Z" + lastUpdateDateTime: "1970-01-04T00:00:00Z", + postDatedLastModifiedSetAt: "1970-01-03T00:00:00Z" } ] } + }, + { + scenarioDescription: "should return an item when it _has_ matured even though the post-dated time is in the future", + inputPrescriptions: { + prescriptionID: "abc", + odsCode: "123" + }, + queryResults: [ + { + itemId: "item_1", + latestStatus: "Ready to collect", + isTerminalState: false, + lastUpdateDateTime: "1970-01-01T00:00:00Z", + postDatedLastModifiedSetAt: futureDateTime + } + ], + expectedResult: { + prescriptionID: "abc", + onboarded: true, + items: [ + { + itemId: "item_1", + latestStatus: "Ready to collect", + isTerminalState: false, + lastUpdateDateTime: "1970-01-01T00:00:00Z", + postDatedLastModifiedSetAt: futureDateTime + } + ] + } + }, + { + scenarioDescription: "should return no items when empty item status are found", + inputPrescriptions: { + prescriptionID: "abc", + odsCode: "123" + }, + queryResults: [], + expectedResult: { + prescriptionID: "abc", + onboarded: false, + items: [] + } } ] describe("Unit tests for buildResults", () => {