From d70a0f6fef90ef190644d4eaf0d92ee19c0d881d Mon Sep 17 00:00:00 2001 From: tstephen-nhs <231503406+tstephen-nhs@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:54:55 +0000 Subject: [PATCH] feat: scaffold app with cdk --- .gitignore | 2 + Makefile | 32 + package-lock.json | 1412 +++++++++++++++-- package.json | 1 + packages/cdk/bin/PfPApiApp.ts | 41 + packages/cdk/bin/PfPApiSandboxApp.ts | 11 + packages/cdk/cdk.json | 76 + packages/cdk/nagSuppressions.ts | 51 + packages/cdk/package.json | 23 + packages/cdk/resources/Apis.ts | 65 + packages/cdk/resources/Functions.ts | 138 ++ .../GetMyPrescriptions.ts | 76 + packages/cdk/resources/StateMachines.ts | 58 + packages/cdk/scripts/deleteMainStacks.ts | 11 + packages/cdk/scripts/deletePrStacks.ts | 10 + packages/cdk/stacks/PfPApiSandboxStack.ts | 10 + packages/cdk/stacks/PfPApiStack.ts | 57 + packages/cdk/tsconfig.json | 30 + 18 files changed, 1930 insertions(+), 174 deletions(-) create mode 100644 packages/cdk/bin/PfPApiApp.ts create mode 100644 packages/cdk/bin/PfPApiSandboxApp.ts create mode 100644 packages/cdk/cdk.json create mode 100644 packages/cdk/nagSuppressions.ts create mode 100644 packages/cdk/package.json create mode 100644 packages/cdk/resources/Apis.ts create mode 100644 packages/cdk/resources/Functions.ts create mode 100644 packages/cdk/resources/StateMachineDefinitions/GetMyPrescriptions.ts create mode 100644 packages/cdk/resources/StateMachines.ts create mode 100644 packages/cdk/scripts/deleteMainStacks.ts create mode 100644 packages/cdk/scripts/deletePrStacks.ts create mode 100644 packages/cdk/stacks/PfPApiSandboxStack.ts create mode 100644 packages/cdk/stacks/PfPApiStack.ts create mode 100644 packages/cdk/tsconfig.json diff --git a/.gitignore b/.gitignore index 3bf963f19..bb7cf1944 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,10 @@ **/public/ **/coverage/ **/node_modules/ +cdk.out/ .#* __pycache__/ +.env .envrc .idea .vscode/settings.json diff --git a/Makefile b/Makefile index a135cc27f..377ee3337 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,20 @@ +SHELL = /bin/bash +.SHELLFLAGS = -o pipefail -c +stack_name ?= pfp-api +export CDK_APP_NAME=PfPApiApp +export CDK_CONFIG_stackName=${stack_name} +export CDK_CONFIG_versionNumber=undefined +export CDK_CONFIG_commitId=undefined +export CDK_CONFIG_isPullRequest=true # Turns off mTLS and drift detection when true +export CDK_CONFIG_environment=dev +export CDK_CONFIG_logRetentionInDays=30 +export CDK_CONFIG_logLevel=DEBUG +export CDK_CONFIG_targetSpineServer=https://example.org +export CDK_CONFIG_targetServiceSearchServer=example.org +export CDK_CONFIG_toggleGetStatusUpdates=false +export CDK_CONFIG_allowNhsNumberOverride=false +export CDK_CONFIG_forwardCsocLogs=false + .PHONY: install build test publish release clean install-node install-python install-hooks sam-build sam-build-sandbox sam-run-local sam-sync sam-sync-sandbox sam-deploy sam-delete sam-list-endpoints sam-list-resources sam-list-outputs sam-validate sam-validate-sandbox sam-deploy-package compile-node compile compile-specification download-get-secrets-layer lint-node lint test clean deep-clean install: install-python install-hooks install-node @@ -104,6 +121,18 @@ sam-deploy-package: guard-artifact_bucket guard-artifact_bucket_prefix guard-sta EnableAlerts=$$ENABLE_ALERTS \ StateMachineLogLevel=$$STATE_MACHINE_LOG_LEVEL +cdk-deploy: + CDK_CONFIG_stackName=${stack_name} REQUIRE_APPROVAL="$${REQUIRE_APPROVAL:-any-change}" npm run cdk-deploy --workspace packages/cdk + +cdk-synth: download-get-secrets-layer + CDK_CONFIG_stackName=${stack_name} npm run cdk-synth --workspace packages/cdk + +cdk-diff: + CDK_CONFIG_stackName=${stack_name} npm run cdk-diff --workspace packages/cdk + +cdk-watch: + CDK_CONFIG_stackName=${stack_name} REQUIRE_APPROVAL="$${REQUIRE_APPROVAL:-any-change}" npm run cdk-watch --workspace packages/cdk + compile-node: npx tsc --build tsconfig.build.json @@ -128,6 +157,7 @@ download-get-secrets-layer: fi lint-node: compile-node + npm run lint --workspace packages/cdk npm run lint --workspace packages/capabilityStatement npm run lint --workspace packages/getMyPrescriptions npm run lint --workspace packages/enrichPrescriptions @@ -142,6 +172,7 @@ lint: lint-node actionlint shellcheck cfn-lint echo "Linting complete" test: compile + npm run test --workspace packages/cdk npm run test --workspace packages/capabilityStatement npm run test --workspace packages/getMyPrescriptions npm run test --workspace packages/enrichPrescriptions @@ -153,6 +184,7 @@ test: compile npm run test --workspace packages/common/testing clean: + rm -rf packages/cdk/coverage rm -rf packages/capabilityStatement/coverage rm -rf packages/getMyPrescriptions/coverage rm -rf packages/enrichPrescriptions/coverage diff --git a/package-lock.json b/package-lock.json index 26166189d..437bae651 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "MIT", "workspaces": [ + "packages/cdk", "packages/capabilityStatement", "packages/getMyPrescriptions", "packages/enrichPrescriptions", @@ -38,6 +39,132 @@ "vitest": "^4.1.0" } }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.263", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.263.tgz", + "integrity": "sha512-X9JvcJhYcb7PHs8R7m4zMablO5C9PGb/hYfLnxds9h/rKJu6l7MiXE/SabCibuehxPnuO/vk+sVVJiUWrccarQ==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.1.tgz", + "integrity": "sha512-We4bmHaowOPHr+IQR4/FyTGjRfjgBj4ICMjtqmJeBDWad3Q/6St12NT07leNtyuukv2qMhtSZJQorD8KpKTwRA==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/cloud-assembly-schema": { + "version": "52.2.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-52.2.0.tgz", + "integrity": "sha512-ourZjixQ/UfsZc7gdk3vt1eHBODMUjQTYYYCY3ZX8fiXyHtWNDAYZPrXUK96jpCC2fLP+tfHTJrBjZ563pmcEw==", + "bundleDependencies": [ + "jsonschema", + "semver" + ], + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "jsonschema": "~1.4.1", + "semver": "^7.7.3" + }, + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { + "version": "7.7.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "license": "Apache-2.0", @@ -205,6 +332,175 @@ } } }, + "node_modules/@aws-sdk/client-cloudformation": { + "version": "3.1011.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudformation/-/client-cloudformation-3.1011.0.tgz", + "integrity": "sha512-v7qixZ+o2+t5f+Y+9qN5EyEejdkGpeDxhkRBaW9xOlE41owhsv7y1EmCsW1Lo5W+0krm0r/09w/TWy7rLHa57Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/credential-provider-node": "^3.972.21", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/region-config-resolver": "^3.972.8", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.7", + "@smithy/config-resolver": "^4.4.11", + "@smithy/core": "^3.23.11", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.25", + "@smithy/middleware-retry": "^4.4.42", + "@smithy/middleware-serde": "^4.2.14", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.4.16", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.41", + "@smithy/util-defaults-mode-node": "^4.2.44", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.13", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-route-53": { + "version": "3.1011.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-route-53/-/client-route-53-3.1011.0.tgz", + "integrity": "sha512-niEdBaAuouO9HVhzv9U7yOsqP3qNQd0F7/igVIu9jXV3yf1ljQMeUa1wAPhZ8X/lIEaXce1Yp7+cdwHTfO8IwQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/credential-provider-node": "^3.972.21", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-sdk-route53": "^3.972.10", + "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/region-config-resolver": "^3.972.8", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.7", + "@smithy/config-resolver": "^4.4.11", + "@smithy/core": "^3.23.11", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.25", + "@smithy/middleware-retry": "^4.4.42", + "@smithy/middleware-serde": "^4.2.14", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.4.16", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.41", + "@smithy/util-defaults-mode-node": "^4.2.44", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.13", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.1011.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1011.0.tgz", + "integrity": "sha512-jY7CGX+vfM/DSi4K8UwaZKoXnhqchmAbKFB1kIuHMfPPqW7l3jC/fUVDb95/njMsB2ymYOTusZEzoCTeUB/4qA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/credential-provider-node": "^3.972.21", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.8", + "@aws-sdk/middleware-expect-continue": "^3.972.8", + "@aws-sdk/middleware-flexible-checksums": "^3.974.0", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-location-constraint": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-sdk-s3": "^3.972.20", + "@aws-sdk/middleware-ssec": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/region-config-resolver": "^3.972.8", + "@aws-sdk/signature-v4-multi-region": "^3.996.8", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.7", + "@smithy/config-resolver": "^4.4.11", + "@smithy/core": "^3.23.11", + "@smithy/eventstream-serde-browser": "^4.2.12", + "@smithy/eventstream-serde-config-resolver": "^4.3.12", + "@smithy/eventstream-serde-node": "^4.2.12", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-blob-browser": "^4.2.13", + "@smithy/hash-node": "^4.2.12", + "@smithy/hash-stream-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/md5-js": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.25", + "@smithy/middleware-retry": "^4.4.42", + "@smithy/middleware-serde": "^4.2.14", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.4.16", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.41", + "@smithy/util-defaults-mode-node": "^4.2.44", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-stream": "^4.5.19", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.13", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/client-secrets-manager": { "version": "3.1009.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.1009.0.tgz", @@ -260,6 +556,7 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/client-ssm/-/client-ssm-3.1009.0.tgz", "integrity": "sha512-xhViVpDZpNyqIj09uSVjNnibeL22FcQUNLh7tZL9zfNzs5/Z/ttBOHMiiVhpXI4TF39012ZMw48zavw62VN2zw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -330,6 +627,19 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/crc64-nvme": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.5.tgz", + "integrity": "sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.972.18", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.18.tgz", @@ -488,28 +798,32 @@ "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@aws-sdk/middleware-bucket-endpoint": { "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz", - "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.8.tgz", + "integrity": "sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/middleware-expect-continue": { "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz", - "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.8.tgz", + "integrity": "sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, @@ -517,17 +831,138 @@ "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.8.tgz", - "integrity": "sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==", + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.974.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.0.tgz", + "integrity": "sha512-BmdDjqvnuYaC4SY7ypHLXfCSsGYGUZkjCLSZyUAAYn1YT28vbNMJNDwhlfkvvE+hQHG5RJDlEmYuvBxcB9jX1g==", "license": "Apache-2.0", "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/crc64-nvme": "^3.972.5", "@aws-sdk/types": "^3.973.6", - "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", - "tslib": "^2.6.2" + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.19", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz", + "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.8.tgz", + "integrity": "sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz", + "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.8.tgz", + "integrity": "sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-route53": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-route53/-/middleware-sdk-route53-3.972.10.tgz", + "integrity": "sha512-eVSTduHxtUd1M/KxKFiAHj1Q9djzx428iPe7Yd8yJdEsgnVH0wPFsCK/ExB0Rv/yIYlVXdBlFJmMj0FgRj4crQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.20.tgz", + "integrity": "sha512-yhva/xL5H4tWQgsBjwV+RRD0ByCzg0TcByDCLp3GXdn/wlyRNfy8zsswDtCvr1WSKQkSQYlyEzPuWkJG0f5HvQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.20", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/core": "^3.23.11", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/smithy-client": "^4.12.5", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.19", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.8.tgz", + "integrity": "sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" @@ -617,6 +1052,23 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.8.tgz", + "integrity": "sha512-n1qYFD+tbqZuyskVaxUE+t10AUz9g3qzDw3Tp6QZDKmqsjfDmZBd4GIk2EKJJNtcCBtE5YiUjDYA+3djFAFBBg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "^3.972.20", + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/token-providers": { "version": "3.1009.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1009.0.tgz", @@ -648,6 +1100,18 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz", + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/util-endpoints": { "version": "3.996.5", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", @@ -1632,6 +2096,21 @@ "uninstall": "^0.0.0" } }, + "node_modules/@nhsdigital/eps-cdk-constructs": { + "version": "1.6.0", + "resolved": "file:nhsdigital-eps-cdk-constructs-1.6.0.tgz", + "integrity": "sha512-pXQR0pWmBeaRdHoD6pLvbjKvDJnrorqaMiemCqK2TfUWM3B77aj14YdeaUo8lAC6tDfNYAr/zzao+DjT04z5fA==", + "license": "MIT", + "dependencies": { + "@aws-sdk/client-cloudformation": "^3.1008.0", + "@aws-sdk/client-route-53": "^3.1008.0", + "@aws-sdk/client-s3": "^3.1008.0", + "aws-cdk": "^2.1111.0", + "aws-cdk-lib": "^2.243.0", + "cdk-nag": "^2.37.52", + "constructs": "^10.5.1" + } + }, "node_modules/@nhsdigital/eps-spine-client": { "version": "2.1.78", "license": "MIT", @@ -1660,6 +2139,7 @@ "version": "1.9.0", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -2565,6 +3045,31 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.2.tgz", + "integrity": "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.3.tgz", + "integrity": "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/config-resolver": { "version": "4.4.11", "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.11.tgz", @@ -2619,6 +3124,76 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.12.tgz", + "integrity": "sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.13.1", + "@smithy/util-hex-encoding": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.12.tgz", + "integrity": "sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.12.tgz", + "integrity": "sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.12.tgz", + "integrity": "sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.12.tgz", + "integrity": "sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/fetch-http-handler": { "version": "5.3.15", "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.15.tgz", @@ -2635,6 +3210,21 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.13.tgz", + "integrity": "sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.2", + "@smithy/chunked-blob-reader-native": "^4.2.3", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/hash-node": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.12.tgz", @@ -2650,6 +3240,20 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.12.tgz", + "integrity": "sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/invalid-dependency": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.12.tgz", @@ -2673,6 +3277,20 @@ "node": ">=18.0.0" } }, + "node_modules/@smithy/md5-js": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.12.tgz", + "integrity": "sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@smithy/middleware-content-length": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.12.tgz", @@ -3223,6 +3841,7 @@ "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -3273,6 +3892,7 @@ "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", @@ -3582,233 +4202,641 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", - "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", - "dev": true, + "node_modules/@vitest/runner": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", + "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.0", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", + "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.0", + "@vitest/utils": "4.1.0", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", + "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", + "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.0", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/apim-spec": { + "resolved": "packages/specification", + "link": true + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", + "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "license": "MIT" + }, + "node_modules/aws-cdk": { + "version": "2.1111.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1111.0.tgz", + "integrity": "sha512-69AVF04cxbAhYzmeJYtUF5bs6ruNnH05EQZJfjadk5Lpg+HVaJY2NjG/2qgsMmKrfRgvLet73Ir8ehVlAaaGng==", + "license": "Apache-2.0", + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/aws-cdk-lib": { + "version": "2.243.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.243.0.tgz", + "integrity": "sha512-qIhg/3gSNeZ9LoVmDATO45HPk+POkoCfPZRezeOPhd2kAJ/wzYswyUcMqpDWXrlRrEVYntxsykQs+2eMA04Isg==", + "bundleDependencies": [ + "@balena/dockerignore", + "@aws-cdk/cloud-assembly-api", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml", + "mime-types" + ], + "license": "Apache-2.0", + "dependencies": { + "@aws-cdk/asset-awscli-v1": "2.2.263", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.1", + "@aws-cdk/cloud-assembly-api": "^2.1.1", + "@aws-cdk/cloud-assembly-schema": "^52.1.0", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.3.3", + "ignore": "^5.3.2", + "jsonschema": "^1.5.0", + "mime-types": "^2.1.35", + "minimatch": "^10.2.3", + "punycode": "^2.3.1", + "semver": "^7.7.4", + "table": "^6.9.0", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 20.0.0" + }, + "peerDependencies": { + "constructs": "^10.5.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@aws-cdk/cloud-assembly-api": { + "version": "2.1.1", + "bundleDependencies": [ + "jsonschema", + "semver" + ], + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "jsonschema": "~1.4.1", + "semver": "^7.7.3" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "@aws-cdk/cloud-assembly-schema": ">=52.1.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@aws-cdk/cloud-assembly-api/node_modules/jsonschema": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/@aws-cdk/cloud-assembly-api/node_modules/semver": { + "version": "7.7.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.18.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "4.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "5.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-uri": { + "version": "3.1.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.3.3", + "inBundle": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.0", - "pathe": "^2.0.3" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=14.14" } }, - "node_modules/@vitest/snapshot": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", - "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.3.2", + "inBundle": true, "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.0", - "@vitest/utils": "4.1.0", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">= 4" } }, - "node_modules/@vitest/spy": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", - "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=8" } }, - "node_modules/@vitest/utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", - "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.2.0", + "inBundle": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.0.3" + "universalify": "^2.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.5.0", + "inBundle": true, "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, "engines": { - "node": ">=6.5" + "node": "*" } }, - "node_modules/acorn": { - "version": "8.16.0", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/mime-db": { + "version": "1.52.0", + "inBundle": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, "engines": { - "node": ">=0.4.0" + "node": ">= 0.6" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/mime-types": { + "version": "2.1.35", + "inBundle": true, "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "dev": true, - "license": "MIT", + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "10.2.4", + "inBundle": true, + "license": "BlueOak-1.0.0", "dependencies": { - "acorn": "^8.11.0" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=0.4.0" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.1", + "inBundle": true, "license": "MIT", "engines": { - "node": ">= 14" + "node": ">=6" } }, - "node_modules/ajv": { - "version": "6.14.0", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "inBundle": true, "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.7.4", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=10" } }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "inBundle": true, "license": "MIT", "dependencies": { - "ajv": "^8.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, - "peerDependencies": { - "ajv": "^8.0.0" + "engines": { + "node": ">=10" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=8" } }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/apim-spec": { - "resolved": "packages/specification", - "link": true - }, - "node_modules/arg": { - "version": "4.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.9.0", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } }, - "node_modules/assertion-error": { + "node_modules/aws-cdk-lib/node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, + "inBundle": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 10.0.0" } }, - "node_modules/ast-v8-to-istanbul": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", - "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.31", - "estree-walker": "^3.0.3", - "js-tokens": "^10.0.0" + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" } }, - "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "license": "MIT" - }, "node_modules/axios": { "version": "1.13.6", "license": "MIT", + "peer": true, "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", @@ -3902,6 +4930,20 @@ "resolved": "packages/capabilityStatement", "link": true }, + "node_modules/cdk": { + "resolved": "packages/cdk", + "link": true + }, + "node_modules/cdk-nag": { + "version": "2.37.55", + "resolved": "https://registry.npmjs.org/cdk-nag/-/cdk-nag-2.37.55.tgz", + "integrity": "sha512-xcAkygwbph3pp7N0UEzJBmXUH/MIsluV7DYJSeZ/V3yCr0Y0QaRGO298WyD6mi4K+Rmnpl+EJoWUxcOblOqLKA==", + "license": "Apache-2.0", + "peerDependencies": { + "aws-cdk-lib": "^2.176.0", + "constructs": "^10.0.5" + } + }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -4039,6 +5081,13 @@ "node": ">= 0.8" } }, + "node_modules/constructs": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.5.1.tgz", + "integrity": "sha512-f/TfFXiS3G/yVIXDjOQn9oTlyu9Wo7Fxyjj7lb8r92iO81jR2uST+9MstxZTmDGx/CgIbxCXkFXgupnLTNxQZg==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4306,6 +5355,7 @@ "integrity": "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -4698,7 +5748,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4759,7 +5808,6 @@ }, "node_modules/get-tsconfig": { "version": "4.13.0", - "dev": true, "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -5516,6 +6564,7 @@ "version": "6.15.0", "dev": true, "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" @@ -6001,6 +7050,7 @@ "version": "19.2.1", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -6009,6 +7059,7 @@ "version": "19.2.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -6142,7 +7193,6 @@ }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" @@ -6395,6 +7445,7 @@ "version": "6.3.9", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@emotion/is-prop-valid": "1.4.0", "@emotion/unitless": "0.10.0", @@ -6594,7 +7645,6 @@ }, "node_modules/tsx": { "version": "4.21.0", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "~0.27.0", @@ -6625,6 +7675,7 @@ "version": "5.9.3", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6707,6 +7758,7 @@ "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.1.0", "@vitest/mocker": "4.1.0", @@ -6845,6 +7897,7 @@ "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@oxc-project/runtime": "0.115.0", "lightningcss": "^1.32.0", @@ -7112,6 +8165,17 @@ "@pfp-common/testing": "^1.0.0" } }, + "packages/cdk": { + "version": "0.1.0", + "dependencies": { + "@nhsdigital/eps-cdk-constructs": "file:../../nhsdigital-eps-cdk-constructs-1.6.0.tgz", + "aws-cdk": "^2.1106.0", + "aws-cdk-lib": "^2.239.0", + "cdk-nag": "^2.37.55", + "constructs": "^10.4.5", + "tsx": "^4.21.0" + } + }, "packages/common/testing": { "name": "@pfp-common/testing", "version": "1.0.0", diff --git a/package.json b/package.json index bfb769df3..608de795c 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "author": "NHS Digital", "license": "MIT", "workspaces": [ + "packages/cdk", "packages/capabilityStatement", "packages/getMyPrescriptions", "packages/enrichPrescriptions", diff --git a/packages/cdk/bin/PfPApiApp.ts b/packages/cdk/bin/PfPApiApp.ts new file mode 100644 index 000000000..01283940a --- /dev/null +++ b/packages/cdk/bin/PfPApiApp.ts @@ -0,0 +1,41 @@ +import { + calculateVersionedStackName, + createApp, + getBooleanConfigFromEnvVar, + getConfigFromEnvVar, + getNumberConfigFromEnvVar +} from "@nhsdigital/eps-cdk-constructs" +import {PfPApiStack} from "../stacks/PfPApiStack" + +function main() { + const {app, props} = createApp({ + productName: "Prescriptions for Patients API", + appName: "PfPApiApp", + repoName: "prescriptionsforpatients", + driftDetectionGroup: "pfp-api" + }) + + const pfpApiStack = new PfPApiStack(app, "PfPApiStack", { + ...props, + stackName: calculateVersionedStackName(getConfigFromEnvVar("stackName"), props), + logRetentionInDays: getNumberConfigFromEnvVar("logRetentionInDays"), + logLevel: getConfigFromEnvVar("logLevel"), + targetSpineServer: getConfigFromEnvVar("targetSpineServer"), + targetServiceSearchServer: getConfigFromEnvVar("targetServiceSearchServer"), + toggleGetStatusUpdates: getConfigFromEnvVar("toggleGetStatusUpdates"), + allowNhsNumberOverride: getConfigFromEnvVar("allowNhsNumberOverride"), + mutualTlsTrustStoreKey: props.isPullRequest ? undefined : getConfigFromEnvVar("trustStoreFile"), + // CSOC API GW log destination - do not change + csocApiGatewayDestination: "arn:aws:logs:eu-west-2:693466633220:destination:api_gateway_log_destination", + forwardCsocLogs: getBooleanConfigFromEnvVar("forwardCsocLogs") + }) + + return pfpApiStack +} + +try { + main() +} catch (error) { + console.error(error) + process.exit(1) +} diff --git a/packages/cdk/bin/PfPApiSandboxApp.ts b/packages/cdk/bin/PfPApiSandboxApp.ts new file mode 100644 index 000000000..e3ebe54c4 --- /dev/null +++ b/packages/cdk/bin/PfPApiSandboxApp.ts @@ -0,0 +1,11 @@ +import {createApp} from "@nhsdigital/eps-cdk-constructs" +import {PfPApiSandboxStack} from "../stacks/PfPApiSandboxStack" + +const {app, props} = createApp({ + productName: "Prescriptions for Patients API", + appName: "PfPApiSandboxApp", + repoName: "prescriptionsforpatients", + driftDetectionGroup: "pfp-api" +}) + +new PfPApiSandboxStack(app, "PfPApiSandboxStack", props) diff --git a/packages/cdk/cdk.json b/packages/cdk/cdk.json new file mode 100644 index 000000000..2fc30f768 --- /dev/null +++ b/packages/cdk/cdk.json @@ -0,0 +1,76 @@ +{ + "watch": { + "include": [ + "packages/**" + ], + "exclude": [ + "**/README.md", + "**/cdk*.json", + "**/*.d.ts", + "**/*.js", + "**/tsconfig.json", + "**/package*.json", + "**/yarn.lock", + "**/node_modules", + "**/tests*", + "**/lib", + "**/coverage", + "**/jest.config.ts", + "**/jest.debug.config.ts" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, + "acknowledged-issue-numbers": [34892] + } +} diff --git a/packages/cdk/nagSuppressions.ts b/packages/cdk/nagSuppressions.ts new file mode 100644 index 000000000..b49660edc --- /dev/null +++ b/packages/cdk/nagSuppressions.ts @@ -0,0 +1,51 @@ +/* eslint-disable max-len */ +import {Stack} from "aws-cdk-lib" +import {safeAddNagSuppressionGroup, safeAddNagSuppression} from "@nhsdigital/eps-cdk-constructs" + +export const nagSuppressions = (stack: Stack) => { + safeAddNagSuppressionGroup( + stack, + [ + "/PfPApiStack/Functions/GetMyPrescriptionsLambda/LambdaPutLogsManagedPolicy/Resource", + "/PfPApiStack/Functions/EnrichPrescriptionsLambda/LambdaPutLogsManagedPolicy/Resource", + "/PfPApiStack/Functions/StatusLambda/LambdaPutLogsManagedPolicy/Resource", + "/PfPApiStack/StateMachines/GetMyPrescriptionsStateMachine/StateMachinePutLogsManagedPolicy/Resource" + ], + [ + { + id: "AwsSolutions-IAM5", + reason: "Suppress error for not having wildcards in permissions. This is a fine as we need to have permissions on all log streams under path" + } + ] + ) + + safeAddNagSuppression( + stack, + "/PfPApiStack/Apis/ApiGateway/ApiGateway/Resource", + [ + { + id: "AwsSolutions-APIG2", + reason: "Suppress error for request validation not being enabled. Validation will be handled by the service logic." + } + ] + ) + + safeAddNagSuppressionGroup( + stack, + [ + "/PfPApiStack/Apis/ApiGateway/ApiGateway/Default/Bundle/GET/Resource", + "/PfPApiStack/Apis/ApiGateway/ApiGateway/Default/_status/GET/Resource" + ], + [ + { + id: "AwsSolutions-APIG4", + reason: "Suppress error for not implementing authorization. Token endpoint should not have an authorizer" + }, + { + id: "AwsSolutions-COG4", + reason: "Suppress error for not implementing a Cognito user pool authorizer. Token endpoint should not have an authorizer" + } + ] + ) + +} diff --git a/packages/cdk/package.json b/packages/cdk/package.json new file mode 100644 index 000000000..35fd4d867 --- /dev/null +++ b/packages/cdk/package.json @@ -0,0 +1,23 @@ +{ + "name": "cdk", + "version": "0.1.0", + "scripts": { + "cdk-synth": "cdk synth --output ../../cdk.out --quiet --app \"npm run tsx -- bin/${CDK_APP_NAME}.ts\"", + "cdk-diff": "cdk diff --app \"npm run tsx -- bin/${CDK_APP_NAME}.ts\"", + "cdk-deploy": "cdk deploy --app \"npm run tsx -- bin/${CDK_APP_NAME}.ts\" --all --ci true --require-approval ${REQUIRE_APPROVAL}", + "cdk-watch": "cdk deploy --app \"npm run tsx -- bin/${CDK_APP_NAME}.ts\" --watch --all --ci true --require-approval ${REQUIRE_APPROVAL}", + "delete-main-stacks": "npm run tsx -- scripts/deleteMainStacks.ts", + "delete-old-pr-stacks": "npm run tsx -- scripts/deletePrStacks.ts", + "lint": "eslint --max-warnings 0 --fix --config ../../eslint.config.mjs .", + "test": "vitest run --coverage", + "tsx": "tsx" + }, + "dependencies": { + "@nhsdigital/eps-cdk-constructs": "file:../../nhsdigital-eps-cdk-constructs-1.6.0.tgz", + "aws-cdk": "^2.1106.0", + "aws-cdk-lib": "^2.239.0", + "cdk-nag": "^2.37.55", + "constructs": "^10.4.5", + "tsx": "^4.21.0" + } +} diff --git a/packages/cdk/resources/Apis.ts b/packages/cdk/resources/Apis.ts new file mode 100644 index 000000000..b1ac0caac --- /dev/null +++ b/packages/cdk/resources/Apis.ts @@ -0,0 +1,65 @@ +import {HttpMethod} from "aws-cdk-lib/aws-lambda" +import { + ExpressStateMachine, + LambdaEndpoint, + RestApiGateway, + StateMachineEndpoint, + TypescriptLambdaFunction +} from "@nhsdigital/eps-cdk-constructs" +import {Construct} from "constructs" + +export interface ApisProps { + readonly stackName: string + readonly logRetentionInDays: number + readonly mutualTlsTrustStoreKey: string | undefined + functions: {[key: string]: TypescriptLambdaFunction} + stateMachines: {[key: string]: ExpressStateMachine} + readonly forwardCsocLogs: boolean + readonly csocApiGatewayDestination: string +} + +export class Apis extends Construct { + apis: {[key: string]: RestApiGateway} + endpoints: {[key: string]: Construct} + + public constructor(scope: Construct, id: string, props: ApisProps){ + super(scope, id) + + const apiGateway = new RestApiGateway(this, "ApiGateway", { + stackName: props.stackName, + logRetentionInDays: props.logRetentionInDays, + mutualTlsTrustStoreKey: props.mutualTlsTrustStoreKey, + forwardCsocLogs: props.forwardCsocLogs, + csocApiGatewayDestination: props.csocApiGatewayDestination, + executionPolicies: [ + props.stateMachines.getMyPrescriptions.executionPolicy, + props.functions.status.executionPolicy + ] + }) + const rootResource = apiGateway.api.root + + const getMyPrescriptionsEndpoint = new StateMachineEndpoint(this, "GetMyPrescriptionsEndpoint", { + parentResource: rootResource, + resourceName: "Bundle", + method: HttpMethod.GET, + restApiGatewayRole: apiGateway.role, + stateMachine: props.stateMachines.getMyPrescriptions + }) + + const statusEndpoint = new LambdaEndpoint(this, "StatusEndpoint", { + parentResource: rootResource, + resourceName: "_status", + method: HttpMethod.GET, + restApiGatewayRole: apiGateway.role, + lambdaFunction: props.functions.status + }) + + this.apis = { + api: apiGateway + } + this.endpoints = { + getMyPrescriptions: getMyPrescriptionsEndpoint, + status: statusEndpoint + } + } +} diff --git a/packages/cdk/resources/Functions.ts b/packages/cdk/resources/Functions.ts new file mode 100644 index 000000000..c8b2d89f7 --- /dev/null +++ b/packages/cdk/resources/Functions.ts @@ -0,0 +1,138 @@ +import {Aws, Fn, RemovalPolicy} from "aws-cdk-lib" +import {ManagedPolicy, PolicyStatement} from "aws-cdk-lib/aws-iam" +import {Construct} from "constructs" +import {TypescriptLambdaFunction} from "@nhsdigital/eps-cdk-constructs" +import {Code, LayerVersion} from "aws-cdk-lib/aws-lambda" +import {join, resolve} from "node:path" + +export interface FunctionsProps { + readonly stackName: string + readonly version: string + readonly commitId: string + readonly deploymentEnvironment: string + readonly targetSpineServer: string + readonly targetServiceSearchServer: string + readonly toggleGetStatusUpdates: string + readonly allowNhsNumberOverride: string + readonly logRetentionInDays: number + readonly logLevel: string +} + +const baseDir = resolve(__dirname, "../../..") + +export class Functions extends Construct { + functions: {[key: string]: TypescriptLambdaFunction} + + public constructor(scope: Construct, id: string, props: FunctionsProps){ + super(scope, id) + + // Imports + const lambdaAccessSecretsPolicy = ManagedPolicy.fromManagedPolicyArn( + this, "lambdaAccessSecretsPolicy", Fn.importValue("account-resources:LambdaAccessSecretsPolicy")) + + const lambdaDecryptSecretsKMSPolicy = ManagedPolicy.fromManagedPolicyArn( + this, "lambdaDecryptSecretsKMSPolicy", Fn.importValue("account-resources:LambdaDecryptSecretsKMSPolicy")) + + const getPfPParametersPolicy = new ManagedPolicy(this, "GetPfPParametersPolicy", { + description: "Read test case SSM parameters", + statements: [ + new PolicyStatement({ + actions: [ + "ssm:GetParameter", + "ssm:GetParameters" + ], + resources: [ + `arn:aws:ssm:${Aws.REGION}:${Aws.ACCOUNT_ID}:parameter/${props.stackName}-TC007NHSNumber`, + `arn:aws:ssm:${Aws.REGION}:${Aws.ACCOUNT_ID}:parameter/${props.stackName}-TC008NHSNumber`, + `arn:aws:ssm:${Aws.REGION}:${Aws.ACCOUNT_ID}:parameter/${props.stackName}-TC009NHSNumber` + ] + }) + ] + }) + + const lambdaDefaultEnvironmentVariables: {[key: string]: string} = { + STACK_NAME: props.stackName, + TargetSpineServer: props.targetSpineServer, + TargetServiceSearchServer: props.targetServiceSearchServer, + SpinePrivateKeyARN: Fn.importValue("account-resources:SpinePrivateKey"), + SpinePublicCertificateARN: Fn.importValue("account-resources:SpinePublicCertificate"), + SpineASIDARN: Fn.importValue("account-resources:SpineASID"), + SpinePartyKeyARN: Fn.importValue("account-resources:SpinePartyKey"), + SpineCAChainARN: Fn.importValue("account-resources:SpineCAChain"), + ServiceSearch3ApiKeyARN: Fn.importValue("pfp-PfP-ServiceSearch-API-Key") + } + + const getSecretsLambdaLayer = new LayerVersion(this, "GetSecretsLambdaLayer", { + description: "get secrets layer", + code: Code.fromAsset(join(baseDir, "packages/getSecretLayer/lib/get-secrets-layer.zip")), + removalPolicy: RemovalPolicy.RETAIN + }) + + // Resources + const getMyPrescriptionsLambda = new TypescriptLambdaFunction(this, "GetMyPrescriptionsLambda", { + functionName: `${props.stackName}-GetMyPrescriptions`, + projectBaseDir: baseDir, + packageBasePath: "packages/getMyPrescriptions", + entryPoint: "src/getMyPrescriptions.ts", + environmentVariables: { + ...lambdaDefaultEnvironmentVariables, + AWS_LAMBDA_EXEC_WRAPPER: "/opt/get-secrets-layer", + DEPLOYMENT_ENVIRONMENT: props.deploymentEnvironment, + GET_STATUS_UPDATES: props.toggleGetStatusUpdates, + ALLOW_NHS_NUMBER_OVERRIDE: props.allowNhsNumberOverride + }, + layers: [getSecretsLambdaLayer], + additionalPolicies: [ + lambdaAccessSecretsPolicy, + lambdaDecryptSecretsKMSPolicy, + getPfPParametersPolicy + ], + logRetentionInDays: props.logRetentionInDays, + logLevel: props.logLevel, + version: props.version, + commitId: props.commitId + }) + + const enrichPrescriptionsLambda = new TypescriptLambdaFunction(this, "EnrichPrescriptionsLambda", { + functionName: `${props.stackName}-EnrichPrescriptions`, + projectBaseDir: baseDir, + packageBasePath: "packages/enrichPrescriptions", + entryPoint: "src/enrichPrescriptions.ts", + environmentVariables: { + ...lambdaDefaultEnvironmentVariables, + DEPLOYMENT_ENVIRONMENT: props.deploymentEnvironment, + EXPECT_STATUS_UPDATES: props.toggleGetStatusUpdates + }, + additionalPolicies: [ + getPfPParametersPolicy + ], + logRetentionInDays: props.logRetentionInDays, + logLevel: props.logLevel, + version: props.version, + commitId: props.commitId + }) + + const statusLambda = new TypescriptLambdaFunction(this, "StatusLambda", { + functionName: `${props.stackName}-status`, + projectBaseDir: baseDir, + packageBasePath: "packages/statusLambda", + entryPoint: "src/statusLambda.ts", + environmentVariables: { + ...lambdaDefaultEnvironmentVariables, + AWS_LAMBDA_EXEC_WRAPPER: "/opt/get-secrets-layer" + }, + layers: [getSecretsLambdaLayer], + additionalPolicies: [lambdaAccessSecretsPolicy, lambdaDecryptSecretsKMSPolicy], + logRetentionInDays: props.logRetentionInDays, + logLevel: props.logLevel, + version: props.version, + commitId: props.commitId + }) + + this.functions = { + getMyPrescriptions: getMyPrescriptionsLambda, + enrichPrescriptions: enrichPrescriptionsLambda, + status: statusLambda + } + } +} diff --git a/packages/cdk/resources/StateMachineDefinitions/GetMyPrescriptions.ts b/packages/cdk/resources/StateMachineDefinitions/GetMyPrescriptions.ts new file mode 100644 index 000000000..3752c280f --- /dev/null +++ b/packages/cdk/resources/StateMachineDefinitions/GetMyPrescriptions.ts @@ -0,0 +1,76 @@ +import {IFunction} from "aws-cdk-lib/aws-lambda" +import { + Chain, + Choice, + Condition, + IChainable, + Pass, + TaskInput +} from "aws-cdk-lib/aws-stepfunctions" +import {LambdaInvoke} from "aws-cdk-lib/aws-stepfunctions-tasks" +import {CatchAllErrorPass} from "@nhsdigital/eps-cdk-constructs" +import {Construct} from "constructs" + +export interface DefinitionProps { + readonly getMyPrescriptionsFunction: IFunction + readonly enrichPrescriptionsFunction: IFunction + readonly getStatusUpdatesFunction: IFunction +} + +export class GetMyPrescriptions extends Construct { + public readonly definition: IChainable + + public constructor(scope: Construct, id: string, props: DefinitionProps){ + super(scope, id) + + const catchAllError = new CatchAllErrorPass(this, "Catch All Error") + + const getMyPrescriptions = new LambdaInvoke(this, "Get My Prescriptions", { + lambdaFunction: props.getMyPrescriptionsFunction, + payload: TaskInput.fromJsonPathAt("$") + }) + getMyPrescriptions.addCatch(catchAllError.state) + + const failedGetMyPrescriptions = new Pass(this, "Failed Get My Prescriptions") + + const parseGetMyPrescriptionsBody = new Pass(this, "Parse Get My Prescriptions Body", { + parameters: { + "body.$": "States.StringToJson($.Payload.body)" + }, + outputPath: "$.body" + }) + + const enrichPrescriptions = new LambdaInvoke(this, "Enrich Prescriptions", { + lambdaFunction: props.enrichPrescriptionsFunction, + payload: TaskInput.fromJsonPathAt("$") + }) + enrichPrescriptions.addCatch(catchAllError.state) + + const getStatusUpdates = new LambdaInvoke(this, "Get Status Updates", { + lambdaFunction: props.getStatusUpdatesFunction, + payload: TaskInput.fromJsonPathAt("$.statusUpdateData"), + resultSelector: { + "Payload.$": "$.Payload" + }, + resultPath: "$.StatusUpdates" + }) + getStatusUpdates.addCatch(enrichPrescriptions, { + resultPath: "$.error" + }) + + const checkGetMyPrescriptionsResult = new Choice(this, "Get My Prescriptions Result") + const evaluateToggleGetStatusUpdates = new Choice(this, "Evaluate Toggle Get Status Updates Parameter") + + const getMyPrescriptionsSucceeded = Condition.numberEquals("$.Payload.statusCode", 200) + const getStatusUpdatesEnabled = Condition.booleanEquals("$.getStatusUpdates", true) + + this.definition = Chain + .start(getMyPrescriptions) + .next(checkGetMyPrescriptionsResult + .when(Condition.not(getMyPrescriptionsSucceeded), failedGetMyPrescriptions) + .otherwise(parseGetMyPrescriptionsBody + .next(evaluateToggleGetStatusUpdates + .when(getStatusUpdatesEnabled, getStatusUpdates.next(enrichPrescriptions)) + .otherwise(enrichPrescriptions)))) + } +} diff --git a/packages/cdk/resources/StateMachines.ts b/packages/cdk/resources/StateMachines.ts new file mode 100644 index 000000000..d7a6a18e6 --- /dev/null +++ b/packages/cdk/resources/StateMachines.ts @@ -0,0 +1,58 @@ +import {Fn} from "aws-cdk-lib" +import {ManagedPolicy, PolicyStatement} from "aws-cdk-lib/aws-iam" +import {Function} from "aws-cdk-lib/aws-lambda" +import {ExpressStateMachine, TypescriptLambdaFunction} from "@nhsdigital/eps-cdk-constructs" +import {Construct} from "constructs" +import {GetMyPrescriptions} from "./StateMachineDefinitions/GetMyPrescriptions" + +export interface StateMachinesProps { + readonly stackName: string + readonly logRetentionInDays: number + functions: {[key: string]: TypescriptLambdaFunction} +} + +export class StateMachines extends Construct { + stateMachines: {[key: string]: ExpressStateMachine} + + public constructor(scope: Construct, id: string, props: StateMachinesProps){ + super(scope, id) + + // Imports + const getStatusUpdates = Function.fromFunctionArn( + this, "GetStatusUpdates", `${Fn.importValue("psu:functions:GetStatusUpdates:FunctionArn")}:$LATEST`) + const callGetStatusUpdatesManagedPolicy = new ManagedPolicy(this, "CallGetStatusUpdatesManagedPolicy", { + description: "call get status updates lambda from get my prescriptions state machine", + statements: [ + new PolicyStatement({ + actions: [ + "lambda:InvokeFunction" + ], + resources: [ + getStatusUpdates.functionArn + ] + }) + ] + }) + + const getMyPrescriptions = new GetMyPrescriptions(this, "GetMyPrescriptionsStateMachineDefinition", { + getMyPrescriptionsFunction: props.functions.getMyPrescriptions.function, + enrichPrescriptionsFunction: props.functions.enrichPrescriptions.function, + getStatusUpdatesFunction: getStatusUpdates + }) + const getMyPrescriptionsStateMachine = new ExpressStateMachine(this, "GetMyPrescriptionsStateMachine", { + stackName: props.stackName, + stateMachineName: `${props.stackName}-GetMyPrescriptions`, + definition: getMyPrescriptions.definition, + logRetentionInDays: props.logRetentionInDays, + additionalPolicies: [ + props.functions.getMyPrescriptions.executionPolicy, + props.functions.enrichPrescriptions.executionPolicy, + callGetStatusUpdatesManagedPolicy + ] + }) + + this.stateMachines = { + getMyPrescriptions: getMyPrescriptionsStateMachine + } + } +} diff --git a/packages/cdk/scripts/deleteMainStacks.ts b/packages/cdk/scripts/deleteMainStacks.ts new file mode 100644 index 000000000..dfb3b28a8 --- /dev/null +++ b/packages/cdk/scripts/deleteMainStacks.ts @@ -0,0 +1,11 @@ +import {deleteUnusedMainStacks, getActiveApiVersions, getConfigFromEnvVar} from "@nhsdigital/eps-cdk-constructs" + +const awsEnvironment = getConfigFromEnvVar("AWS_ENVIRONMENT", "") +deleteUnusedMainStacks( + "pfp-api", + () => getActiveApiVersions("prescriptions-for-patients"), + `${awsEnvironment}.eps.national.nhs.uk.` +).catch((error) => { + console.error(error) + process.exit(1) +}) diff --git a/packages/cdk/scripts/deletePrStacks.ts b/packages/cdk/scripts/deletePrStacks.ts new file mode 100644 index 000000000..39977cbda --- /dev/null +++ b/packages/cdk/scripts/deletePrStacks.ts @@ -0,0 +1,10 @@ +import {deleteUnusedPrStacks} from "@nhsdigital/eps-cdk-constructs" + +deleteUnusedPrStacks( + "pfp-api", + "prescriptionsforpatients", + "dev.eps.national.nhs.uk." +).catch((error) => { + console.error(error) + process.exit(1) +}) diff --git a/packages/cdk/stacks/PfPApiSandboxStack.ts b/packages/cdk/stacks/PfPApiSandboxStack.ts new file mode 100644 index 000000000..49d392259 --- /dev/null +++ b/packages/cdk/stacks/PfPApiSandboxStack.ts @@ -0,0 +1,10 @@ +import {Stack, App} from "aws-cdk-lib" +import {StandardStackProps} from "@nhsdigital/eps-cdk-constructs" + +export class PfPApiSandboxStack extends Stack { + public constructor(scope: App, id: string, props: StandardStackProps){ + super(scope, id, props) + + // PLACEHOLDER FOR SANDBOX RESOURCES + } +} diff --git a/packages/cdk/stacks/PfPApiStack.ts b/packages/cdk/stacks/PfPApiStack.ts new file mode 100644 index 000000000..b6f5fa364 --- /dev/null +++ b/packages/cdk/stacks/PfPApiStack.ts @@ -0,0 +1,57 @@ +import {App, Stack} from "aws-cdk-lib" +import {nagSuppressions} from "../nagSuppressions" +import {Functions} from "../resources/Functions" +import {StateMachines} from "../resources/StateMachines" +import {Apis} from "../resources/Apis" +import {StandardStackProps} from "@nhsdigital/eps-cdk-constructs" + +export interface PfPApiStackProps extends StandardStackProps { + readonly stackName: string + readonly logRetentionInDays: number + readonly logLevel: string + readonly targetSpineServer: string + readonly targetServiceSearchServer: string + readonly toggleGetStatusUpdates: string + readonly allowNhsNumberOverride: string + readonly mutualTlsTrustStoreKey: string | undefined + readonly csocApiGatewayDestination: string + readonly forwardCsocLogs: boolean +} + +export class PfPApiStack extends Stack { + public constructor(scope: App, id: string, props: PfPApiStackProps){ + super(scope, id, props) + + // Resources + const functions = new Functions(this, "Functions", { + stackName: props.stackName, + version: props.version, + commitId: props.commitId, + deploymentEnvironment: props.environment, + targetSpineServer: props.targetSpineServer, + targetServiceSearchServer: props.targetServiceSearchServer, + toggleGetStatusUpdates: props.toggleGetStatusUpdates, + allowNhsNumberOverride: props.allowNhsNumberOverride, + logRetentionInDays: props.logRetentionInDays, + logLevel: props.logLevel + }) + + const stateMachines = new StateMachines(this, "StateMachines", { + stackName: props.stackName, + logRetentionInDays: props.logRetentionInDays, + functions: functions.functions + }) + + new Apis(this, "Apis", { + stackName: props.stackName, + logRetentionInDays: props.logRetentionInDays, + mutualTlsTrustStoreKey: props.mutualTlsTrustStoreKey, + functions: functions.functions, + stateMachines: stateMachines.stateMachines, + csocApiGatewayDestination: props.csocApiGatewayDestination, + forwardCsocLogs: props.forwardCsocLogs + }) + + nagSuppressions(this) + } +} diff --git a/packages/cdk/tsconfig.json b/packages/cdk/tsconfig.json new file mode 100644 index 000000000..b24f926cd --- /dev/null +++ b/packages/cdk/tsconfig.json @@ -0,0 +1,30 @@ +{ + "extends": "../../tsconfig.defaults.json", + "compilerOptions": { + "module": "commonjs", + "rootDir": ".", + "outDir": "lib", + "allowImportingTsExtensions": true, + "noEmit": true, + "strict": false, + "lib": [ + "es2020" + ], + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "../../node_modules/@types" + ] + }, + "include": ["resources/**/*", "constructs/**/*", "policies/**/*", "stacks/**/*", "tests/**/*", "scripts/**/*", "nagSuppressions.ts"], + "exclude": ["node_modules", "cdk.out"] +}