diff --git a/package-lock.json b/package-lock.json index e3c5c285..5a07c4a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1224,6 +1224,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -4280,6 +4281,7 @@ "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -4343,6 +4345,7 @@ "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/types": "8.54.0", @@ -4972,6 +4975,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5661,6 +5665,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -5943,7 +5948,8 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.5.tgz", "integrity": "sha512-fOoP70YLevMZr5avJHx2DU3LNYmC6wM8OwdrNewMZou1kZnPGOeVzBrRjZNgFDHUlulYUjkpFRSpTE3D+n+ZSg==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/convert-source-map": { "version": "2.0.0", @@ -6187,6 +6193,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7096,6 +7103,7 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -9523,6 +9531,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -9661,6 +9670,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -9747,6 +9757,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9908,6 +9919,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -10016,6 +10028,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -10029,6 +10042,7 @@ "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", @@ -10422,6 +10436,9 @@ "aws-cdk-lib": "^2.236.0", "cdk-nag": "^2.37.52", "constructs": "^10.4.5" + }, + "devDependencies": { + "@types/node": "^25.0.10" } }, "packages/deploymentUtils": { diff --git a/packages/cdkConstructs/package.json b/packages/cdkConstructs/package.json index 061c6a91..c4a659e9 100644 --- a/packages/cdkConstructs/package.json +++ b/packages/cdkConstructs/package.json @@ -35,5 +35,8 @@ "lib" ], "main": "lib/src/index.js", - "types": "lib/src/index.d.ts" + "types": "lib/src/index.d.ts", + "devDependencies": { + "@types/node": "^25.0.10" + } } diff --git a/packages/cdkConstructs/src/apps/createApp.ts b/packages/cdkConstructs/src/apps/createApp.ts index 39866e89..611686b4 100644 --- a/packages/cdkConstructs/src/apps/createApp.ts +++ b/packages/cdkConstructs/src/apps/createApp.ts @@ -12,19 +12,37 @@ export interface StandardStackProps extends StackProps { readonly version: string readonly commitId: string readonly isPullRequest: boolean + readonly environment: string } -export function createApp( - appName: string, - repoName: string, - driftDetectionGroup: string, - isStateless: boolean = true, - region: string = "eu-west-2" -): {app: App, props: StandardStackProps} { +export interface CreateAppParams { + readonly productName: string + readonly appName: string + readonly repoName: string + readonly driftDetectionGroup: string + readonly isStateless?: boolean + readonly region?: string + readonly projectType?: string + readonly publicFacing?: string + readonly serviceCategory?: string +} + +export function createApp({ + productName, + appName, + repoName, + driftDetectionGroup, + isStateless = true, + region = "eu-west-2", + projectType = "Production", + publicFacing = "Y", + serviceCategory = "Platinum" +}: CreateAppParams): { app: App, props: StandardStackProps } { let stackName = getConfigFromEnvVar("stackName") const versionNumber = getConfigFromEnvVar("versionNumber") const commitId = getConfigFromEnvVar("commitId") const isPullRequest = getBooleanConfigFromEnvVar("isPullRequest") + const environment = getConfigFromEnvVar("environment") let cfnDriftDetectionGroup = driftDetectionGroup if (isPullRequest) { cfnDriftDetectionGroup += "-pull-request" @@ -34,6 +52,20 @@ export function createApp( Aspects.of(app).add(new AwsSolutionsChecks({verbose: true})) + Tags.of(app).add("TagVersion", "1") + Tags.of(app).add("Programme", "EPS") + Tags.of(app).add("Product", productName) + Tags.of(app).add("Owner", "england.epssupport@nhs.net") + Tags.of(app).add("CostCentre", "128997") + Tags.of(app).add("Customer", "NHS England") + Tags.of(app).add("data_classification", "5") + Tags.of(app).add("DataType", "PII") + Tags.of(app).add("Environment", environment) + Tags.of(app).add("ProjectType", projectType) + Tags.of(app).add("PublicFacing", publicFacing) + Tags.of(app).add("ServiceCategory", serviceCategory) + Tags.of(app).add("OnOffPattern", "AlwaysOn") + Tags.of(app).add("DeploymentTool", "CDK") Tags.of(app).add("version", versionNumber) Tags.of(app).add("commit", commitId) Tags.of(app).add("stackName", stackName) @@ -54,7 +86,8 @@ export function createApp( stackName, version: versionNumber, commitId, - isPullRequest + isPullRequest, + environment } } } diff --git a/packages/cdkConstructs/tests/apps/createApp.test.ts b/packages/cdkConstructs/tests/apps/createApp.test.ts index e09dbf4f..78fd87f0 100644 --- a/packages/cdkConstructs/tests/apps/createApp.test.ts +++ b/packages/cdkConstructs/tests/apps/createApp.test.ts @@ -22,11 +22,21 @@ import { expect, vi } from "vitest" -import {createApp} from "../../src/apps/createApp" +import {createApp, type CreateAppParams} from "../../src/apps/createApp" import {AwsSolutionsChecks} from "cdk-nag" describe("createApp", () => { const originalEnv = process.env + const defaultParams: CreateAppParams = { + productName: "testProduct", + appName: "testApp", + repoName: "testRepo", + driftDetectionGroup: "test-drift-group" + } + const buildParams = (overrides: Partial = {}): CreateAppParams => ({ + ...defaultParams, + ...overrides + }) beforeEach(() => { // Reset environment before each test @@ -45,10 +55,11 @@ describe("createApp", () => { process.env.CDK_CONFIG_versionNumber = "1.2.3" process.env.CDK_CONFIG_commitId = "abc123def456" process.env.CDK_CONFIG_isPullRequest = "false" + process.env.CDK_CONFIG_environment = "test-environment" }) test("creates an App with correct configuration", () => { - const {app, props} = createApp("testApp", "testRepo", "test-drift-group") + const {app, props} = createApp(buildParams()) expect(app).toBeInstanceOf(App) expect(props.stackName).toBe("test-stack-1-2-3") @@ -59,13 +70,13 @@ describe("createApp", () => { }) test("creates stateful App with correct stackName", () => { - const {props} = createApp("testApp", "testRepo", "test-drift-group", false) + const {props} = createApp(buildParams({isStateless: false})) expect(props.stackName).toBe("test-stack") }) test("uses custom region when provided", () => { - const {props} = createApp("testApp", "testRepo", "test-drift-group", true, "us-east-1") + const {props} = createApp(buildParams({region: "us-east-1"})) expect(props.env?.region).toBe("us-east-1") }) @@ -77,12 +88,27 @@ describe("createApp", () => { add: addTagSpy } as unknown as Tags) - const {app} = createApp("testApp", "testRepo", "test-drift-group") + const {app} = createApp(buildParams()) // Verify Tags.of was called with the app expect(tagsOfSpy).toHaveBeenCalledWith(app) // Verify all expected tags were added with correct values + expect(addTagSpy).toHaveBeenCalledWith("TagVersion", "1") + expect(addTagSpy).toHaveBeenCalledWith("Programme", "EPS") + expect(addTagSpy).toHaveBeenCalledWith("Product", "testProduct") + expect(addTagSpy).toHaveBeenCalledWith("Owner", "england.epssupport@nhs.net") + expect(addTagSpy).toHaveBeenCalledWith("Product", "testProduct") + expect(addTagSpy).toHaveBeenCalledWith("CostCentre", "128997") + expect(addTagSpy).toHaveBeenCalledWith("Customer", "NHS England") + expect(addTagSpy).toHaveBeenCalledWith("data_classification", "5") + expect(addTagSpy).toHaveBeenCalledWith("DataType", "PII") + expect(addTagSpy).toHaveBeenCalledWith("Environment", "test-environment") + expect(addTagSpy).toHaveBeenCalledWith("ProjectType", "Production") + expect(addTagSpy).toHaveBeenCalledWith("PublicFacing", "Y") + expect(addTagSpy).toHaveBeenCalledWith("ServiceCategory", "Platinum") + expect(addTagSpy).toHaveBeenCalledWith("OnOffPattern", "AlwaysOn") + expect(addTagSpy).toHaveBeenCalledWith("DeploymentTool", "CDK") expect(addTagSpy).toHaveBeenCalledWith("version", "1.2.3") expect(addTagSpy).toHaveBeenCalledWith("commit", "abc123def456") expect(addTagSpy).toHaveBeenCalledWith("stackName", "test-stack") @@ -90,15 +116,15 @@ describe("createApp", () => { expect(addTagSpy).toHaveBeenCalledWith("repo", "testRepo") expect(addTagSpy).toHaveBeenCalledWith("cfnDriftDetectionGroup", "test-drift-group") - // Verify exactly 6 tags were added - expect(addTagSpy).toHaveBeenCalledTimes(6) + // Verify exactly 20 tags were added + expect(addTagSpy).toHaveBeenCalledTimes(20) // Restore the spy tagsOfSpy.mockRestore() }) test("adds AwsSolutionsChecks aspect", () => { - const {app} = createApp("testApp", "testRepo", "test-drift-group") + const {app} = createApp(buildParams()) const aspects = Aspects.of(app).all expect(aspects).toContainEqual(new AwsSolutionsChecks({verbose: true})) @@ -111,19 +137,21 @@ describe("createApp", () => { process.env.CDK_CONFIG_versionNumber = "0.0.1-pr" process.env.CDK_CONFIG_commitId = "pr123" process.env.CDK_CONFIG_isPullRequest = "true" + process.env.CDK_CONFIG_environment = "test-environment" }) test("correctly modifies props", () => { - const {props} = createApp("testApp", "testRepo", "test-drift-group") + const {props} = createApp(buildParams()) expect(props.stackName).toBe("pr-stack") expect(props.version).toBe("0.0.1-pr") expect(props.commitId).toBe("pr123") expect(props.isPullRequest).toBe(true) + expect(props.environment).toBe("test-environment") }) test("creates stateful App with unmodified stackName", () => { - const {props} = createApp("testApp", "testRepo", "test-drift-group", false) + const {props} = createApp(buildParams({isStateless: false})) expect(props.stackName).toBe("pr-stack") }) @@ -135,7 +163,7 @@ describe("createApp", () => { add: addTagSpy } as unknown as Tags) - const {app} = createApp("testApp", "testRepo", "test-drift-group") + const {app} = createApp(buildParams()) // Verify Tags.of was called with the app expect(tagsOfSpy).toHaveBeenCalledWith(app) @@ -150,9 +178,10 @@ describe("createApp", () => { process.env.CDK_CONFIG_versionNumber = "1.0.0" process.env.CDK_CONFIG_commitId = "abc123" process.env.CDK_CONFIG_isPullRequest = "false" + process.env.CDK_CONFIG_environment = "test-environment" expect(() => { - createApp("testApp", "testRepo", "test-drift-group") + createApp(buildParams()) }).toThrow("Environment variable CDK_CONFIG_stackName is not set") }) @@ -160,9 +189,10 @@ describe("createApp", () => { process.env.CDK_CONFIG_stackName = "test-stack" process.env.CDK_CONFIG_commitId = "abc123" process.env.CDK_CONFIG_isPullRequest = "false" + process.env.CDK_CONFIG_environment = "test-environment" expect(() => { - createApp("testApp", "testRepo", "test-drift-group") + createApp(buildParams()) }).toThrow("Environment variable CDK_CONFIG_versionNumber is not set") }) @@ -170,9 +200,10 @@ describe("createApp", () => { process.env.CDK_CONFIG_stackName = "test-stack" process.env.CDK_CONFIG_versionNumber = "1.0.0" process.env.CDK_CONFIG_isPullRequest = "false" + process.env.CDK_CONFIG_environment = "test-environment" expect(() => { - createApp("testApp", "testRepo", "test-drift-group") + createApp(buildParams()) }).toThrow("Environment variable CDK_CONFIG_commitId is not set") }) @@ -180,10 +211,21 @@ describe("createApp", () => { process.env.CDK_CONFIG_stackName = "test-stack" process.env.CDK_CONFIG_versionNumber = "1.0.0" process.env.CDK_CONFIG_commitId = "abc123" - + process.env.CDK_CONFIG_environment = "test-environment" expect(() => { - createApp("testApp", "testRepo", "test-drift-group") + createApp(buildParams()) }).toThrow("Environment variable CDK_CONFIG_isPullRequest is not set") }) + + test("throws error when environment is not set", () => { + process.env.CDK_CONFIG_stackName = "test-stack" + process.env.CDK_CONFIG_versionNumber = "1.0.0" + process.env.CDK_CONFIG_commitId = "abc123" + process.env.CDK_CONFIG_isPullRequest = "false" + expect(() => { + createApp(buildParams()) + }).toThrow("Environment variable CDK_CONFIG_environment is not set") + }) + }) })