Skip to content

Commit 434dccc

Browse files
Added stack name calculation for stateless stacks
1 parent 77e31b8 commit 434dccc

5 files changed

Lines changed: 42 additions & 24 deletions

File tree

packages/cdkConstructs/src/apps/createApp.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
StackProps
66
} from "aws-cdk-lib"
77
import {AwsSolutionsChecks} from "cdk-nag"
8-
import {getConfigFromEnvVar, getBooleanConfigFromEnvVar} from "../config"
8+
import {getConfigFromEnvVar, getBooleanConfigFromEnvVar, calculateVersionedStackName} from "../config"
99

1010
export interface StandardStackProps extends StackProps {
1111
readonly stackName: string
@@ -18,9 +18,10 @@ export function createApp(
1818
appName: string,
1919
repoName: string,
2020
driftDetectionGroup: string,
21+
isStateless: boolean = true,
2122
region: string = "eu-west-2"
2223
): {app: App, props: StandardStackProps} {
23-
const stackName = getConfigFromEnvVar("stackName")
24+
let stackName = getConfigFromEnvVar("stackName")
2425
const versionNumber = getConfigFromEnvVar("versionNumber")
2526
const commitId = getConfigFromEnvVar("commitId")
2627
const isPullRequest = getBooleanConfigFromEnvVar("isPullRequest")
@@ -40,6 +41,10 @@ export function createApp(
4041
Tags.of(app).add("repo", repoName)
4142
Tags.of(app).add("cfnDriftDetectionGroup", cfnDriftDetectionGroup)
4243

44+
if (isStateless && !isPullRequest) {
45+
stackName = calculateVersionedStackName(stackName, versionNumber)
46+
}
47+
4348
return {
4449
app,
4550
props: {

packages/cdkConstructs/src/config/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,7 @@ export function getBooleanCFConfigValue(exports: Record<string, string>, exportN
6464
const value = getCFConfigValue(exports, exportName)
6565
return value.toLowerCase() === "true"
6666
}
67+
68+
export function calculateVersionedStackName(baseStackName: string, version: string): string {
69+
return `${baseStackName}-${version.replace(/\./g, "-")}`
70+
}

packages/cdkConstructs/src/specifications/deployApi.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
import {LambdaClient, InvokeCommand} from "@aws-sdk/client-lambda"
3-
import {getCFConfigValue, getCloudFormationExports} from "../config"
3+
import {getCFConfigValue, getCloudFormationExports, calculateVersionedStackName} from "../config"
44

55
export type ApiConfig = {
66
specification: string
@@ -61,6 +61,7 @@ export async function deployApi(
6161
spec["x-nhsd-apim"].monitoring = false
6262
delete spec["x-nhsd-apim"].target.security.secret
6363
} else {
64+
stackName = calculateVersionedStackName(stackName, version)
6465
spec["x-nhsd-apim"].target.security.secret = mtlsSecretName
6566
}
6667
spec.info.version = version

packages/cdkConstructs/tests/apps/createApp.test.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,21 @@ describe("createApp", () => {
5151
const {app, props} = createApp("testApp", "testRepo", "test-drift-group")
5252

5353
expect(app).toBeInstanceOf(App)
54-
expect(props.stackName).toBe("test-stack")
54+
expect(props.stackName).toBe("test-stack-1-2-3")
5555
expect(props.version).toBe("1.2.3")
5656
expect(props.commitId).toBe("abc123def456")
5757
expect(props.isPullRequest).toBe(false)
5858
expect(props.env?.region).toBe("eu-west-2")
5959
})
6060

61+
test("creates stateful App with correct stackName", () => {
62+
const {props} = createApp("testApp", "testRepo", "test-drift-group", false)
63+
64+
expect(props.stackName).toBe("test-stack")
65+
})
66+
6167
test("uses custom region when provided", () => {
62-
const {props} = createApp("testApp", "testRepo", "test-drift-group", "us-east-1")
68+
const {props} = createApp("testApp", "testRepo", "test-drift-group", true, "us-east-1")
6369

6470
expect(props.env?.region).toBe("us-east-1")
6571
})
@@ -107,12 +113,21 @@ describe("createApp", () => {
107113
process.env.CDK_CONFIG_isPullRequest = "true"
108114
})
109115

110-
test("sets isPullRequest to true in props", () => {
116+
test("correctly modifies props", () => {
111117
const {props} = createApp("testApp", "testRepo", "test-drift-group")
112118

119+
expect(props.stackName).toBe("pr-stack")
120+
expect(props.version).toBe("0.0.1-pr")
121+
expect(props.commitId).toBe("pr123")
113122
expect(props.isPullRequest).toBe(true)
114123
})
115124

125+
test("creates stateful App with unmodified stackName", () => {
126+
const {props} = createApp("testApp", "testRepo", "test-drift-group", false)
127+
128+
expect(props.stackName).toBe("pr-stack")
129+
})
130+
116131
test("modifies drift detection group with -pull-request suffix", () => {
117132
// Spy on Tags.of(app).add to verify tag calls
118133
const addTagSpy = vi.fn()

packages/cdkConstructs/tests/specifications/deployApi.test.ts

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,15 @@ vi.mock("@aws-sdk/client-lambda", () => {
3030
return {LambdaClient, InvokeCommand}
3131
})
3232

33-
const getCloudFormationExportsMock = vi.fn()
33+
const getCloudFormationExportsMock = vi.hoisted(() => vi.fn())
3434

35-
vi.mock("../../src/config", () => ({
36-
getCloudFormationExports: () => getCloudFormationExportsMock(),
37-
getCFConfigValue: (exports: Record<string, string>, name: string) => exports[name]
38-
}))
35+
vi.mock("../../src/config", async (importOriginal) => {
36+
const originalModule = await importOriginal<typeof import("../../src/config")>()
37+
return {
38+
...originalModule,
39+
getCloudFormationExports: getCloudFormationExportsMock
40+
}
41+
})
3942

4043
function createSpec() {
4144
return {
@@ -94,17 +97,15 @@ function functionNameFromCall(callIndex: number) {
9497
describe("deployApi", () => {
9598
beforeEach(() => {
9699
lambdaSendMock.mockReset().mockResolvedValue({Payload: Buffer.from('"ok"')})
97-
getCloudFormationExportsMock.mockReset()
100+
getCloudFormationExportsMock.mockReset().mockResolvedValue(defaultExportsMap)
98101
})
99102

100103
test("stores secrets, deploys instance and publishes spec for internal-dev", async () => {
101-
getCloudFormationExportsMock.mockResolvedValue(defaultExportsMap)
102-
103104
await deployApi(
104105
buildConfig({
105106
version: "2.0.0",
106107
apigeeEnvironment: "internal-dev",
107-
stackName: "eps-stack-001"
108+
stackName: "eps-stack"
108109
}),
109110
false
110111
)
@@ -130,7 +131,7 @@ describe("deployApi", () => {
130131
expect(instancePayload.specDefinition.info.version).toBe("2.0.0")
131132
expect(instancePayload.specDefinition["x-nhsd-apim"].target.security.secret).toBe("mtls/secret")
132133
expect(instancePayload.specDefinition["x-nhsd-apim"].target.url)
133-
.toBe("https://eps-stack-001.nonprod.eps.national.nhs.uk")
134+
.toBe("https://eps-stack-2-0-0.nonprod.eps.national.nhs.uk")
134135
expect(instancePayload.specDefinition.components.securitySchemes["nhs-cis2-aal3"].$ref)
135136
.toBe("https://proxygen.ptl.api.platform.nhs.uk/components/securitySchemes/nhs-cis2-aal3")
136137
expect(instancePayload.specDefinition.servers[0].url)
@@ -144,8 +145,6 @@ describe("deployApi", () => {
144145
})
145146

146147
test("handles pull requests in sandbox without storing secrets", async () => {
147-
getCloudFormationExportsMock.mockResolvedValue(defaultExportsMap)
148-
149148
await deployApi(
150149
buildConfig({
151150
version: "3.1.4",
@@ -171,8 +170,6 @@ describe("deployApi", () => {
171170
})
172171

173172
test("uses prod lambdas and prod security scheme refs", async () => {
174-
getCloudFormationExportsMock.mockResolvedValue(defaultExportsMap)
175-
176173
await deployApi(
177174
buildConfig({
178175
version: "4.0.0",
@@ -196,8 +193,6 @@ describe("deployApi", () => {
196193
})
197194

198195
test("publishes spec to prod catalogue for int environment", async () => {
199-
getCloudFormationExportsMock.mockResolvedValue(defaultExportsMap)
200-
201196
await deployApi(
202197
buildConfig({
203198
version: "5.0.0",
@@ -217,7 +212,6 @@ describe("deployApi", () => {
217212
})
218213

219214
test("dry run only logs intended invocations", async () => {
220-
getCloudFormationExportsMock.mockResolvedValue(defaultExportsMap)
221215
const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined)
222216

223217
await deployApi(
@@ -237,7 +231,6 @@ describe("deployApi", () => {
237231
})
238232

239233
test("throws when lambda invocation returns a FunctionError", async () => {
240-
getCloudFormationExportsMock.mockResolvedValue(defaultExportsMap)
241234
lambdaSendMock
242235
.mockResolvedValueOnce({FunctionError: "Handled", Payload: Buffer.from('"bad"')})
243236

0 commit comments

Comments
 (0)