From 8dff3e984539be9027f6cf111e0b31292011e8ab Mon Sep 17 00:00:00 2001 From: Akash Askoolum Date: Wed, 23 Dec 2020 09:13:35 +0000 Subject: [PATCH 1/3] add parameter helper functions on GuStack Helper functions to get/set parameters on the GuStack. An `Error` is thrown when getting a parameter that hasn't been set; this is to catch the error at compile time. --- src/constructs/core/parameters.ts | 5 +++++ src/constructs/core/stack.test.ts | 18 ++++++++++++++++++ src/constructs/core/stack.ts | 17 +++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/constructs/core/parameters.ts b/src/constructs/core/parameters.ts index 975efd2aaa..ba93880dd6 100644 --- a/src/constructs/core/parameters.ts +++ b/src/constructs/core/parameters.ts @@ -10,11 +10,16 @@ export interface GuParameterProps extends CfnParameterProps { export type GuNoTypeParameterProps = Omit; export class GuParameter extends CfnParameter { + public readonly id: string; + constructor(scope: GuStack, id: string, props: GuParameterProps) { super(scope, id, { ...props, type: props.fromSSM ? `AWS::SSM::Parameter::Value<${props.type ?? "String"}>` : props.type, }); + + this.id = id; + scope.setParam(this); } } diff --git a/src/constructs/core/stack.test.ts b/src/constructs/core/stack.test.ts index e4bf1cb667..59e21867d4 100644 --- a/src/constructs/core/stack.test.ts +++ b/src/constructs/core/stack.test.ts @@ -7,6 +7,7 @@ import { alphabeticalTags, simpleGuStackForTesting } from "../../../test/utils"; import type { SynthedStack } from "../../../test/utils"; import { Stage, Stages } from "../../constants"; import { TrackingTag } from "../../constants/library-info"; +import { GuParameter } from "./parameters"; import { GuStack } from "./stack"; describe("The GuStack construct", () => { @@ -63,4 +64,21 @@ describe("The GuStack construct", () => { expect(stack.app).toBe("MyApp"); }); + + it("should return a parameter that exists", () => { + const stack = new GuStack(new App(), "Test", { app: "MyApp", stack: "test" }); + const testParam = new GuParameter(stack, "MyTestParam", {}); + stack.setParam(testParam); + + const actual = stack.getParam("MyTestParam"); + expect(actual).toBe(testParam); + }); + + it("should throw on attempt to get a parameter that doesn't exist", () => { + const stack = new GuStack(new App(), "Test", { app: "MyApp", stack: "test" }); + + expect(() => stack.getParam("i-do-not-exist")).toThrowError( + "Attempting to read parameter i-do-not-exist which does not exist" + ); + }); }); diff --git a/src/constructs/core/stack.ts b/src/constructs/core/stack.ts index 0780b4eb8f..df1a97f031 100644 --- a/src/constructs/core/stack.ts +++ b/src/constructs/core/stack.ts @@ -1,6 +1,7 @@ import type { App, StackProps } from "@aws-cdk/core"; import { Stack, Tags } from "@aws-cdk/core"; import { TrackingTag } from "../../constants/library-info"; +import type { GuParameter } from "./parameters"; import { GuStageParameter } from "./parameters"; export interface GuStackProps extends StackProps { @@ -43,6 +44,8 @@ export class GuStack extends Stack { private readonly _stack: string; private readonly _app: string; + private params: Map; + public readonly migratedFromCloudFormation: boolean; get stage(): string { @@ -73,12 +76,26 @@ export class GuStack extends Stack { Tags.of(this).add(key, value, { applyToLaunchedInstances }); } + setParam(value: GuParameter): void { + this.params.set(value.id, value); + } + + getParam(key: string): T { + if (!this.params.has(key)) { + throw new Error(`Attempting to read parameter ${key} which does not exist`); + } + + return this.params.get(key) as T; + } + // eslint-disable-next-line custom-rules/valid-constructors -- GuStack is the exception as it must take an App constructor(app: App, id: string, props: GuStackProps) { super(app, id, props); this.migratedFromCloudFormation = !!props.migratedFromCloudFormation; + this.params = new Map(); + this._stage = new GuStageParameter(this); this._stack = props.stack; this._app = props.app; From dcac71de63f7fbca1c97cb3d72ccca6f8f035cb7 Mon Sep 17 00:00:00 2001 From: Akash Askoolum Date: Fri, 12 Feb 2021 16:47:31 +0000 Subject: [PATCH 2/3] remove explicit testing of GuStageParameter and GuStackParameter A GuParameter now relies on a function from GuStack, as GuStageParameter and GuStackParameter are automatically added to a GuStack, we cannot explicitly test them now as its recursuve. Remove them in favour of a slightly implicit test in stack.test.ts. --- src/constructs/core/parameters.test.ts | 38 -------------------------- 1 file changed, 38 deletions(-) diff --git a/src/constructs/core/parameters.test.ts b/src/constructs/core/parameters.test.ts index ff7c53b60b..55b0fa0d10 100644 --- a/src/constructs/core/parameters.test.ts +++ b/src/constructs/core/parameters.test.ts @@ -1,22 +1,17 @@ import "@aws-cdk/assert/jest"; import { SynthUtils } from "@aws-cdk/assert/lib/synth-utils"; -import { Stack } from "@aws-cdk/core"; import { simpleGuStackForTesting } from "../../../test/utils"; import type { SynthedStack } from "../../../test/utils"; -import { Stage, Stages } from "../../constants"; import { GuAmiParameter, GuArnParameter, GuInstanceTypeParameter, GuParameter, GuS3ObjectArnParameter, - GuStackParameter, - GuStageParameter, GuStringParameter, GuSubnetListParameter, GuVpcParameter, } from "./parameters"; -import type { GuStack } from "./stack"; describe("The GuParameter class", () => { it("sets the type as passed through by default", () => { @@ -84,39 +79,6 @@ describe("The GuStringParameter class", () => { }); }); -describe("The GuStageParameter class", () => { - it("should set the values as required", () => { - const stack = new Stack() as GuStack; - - new GuStageParameter(stack); - - const json = SynthUtils.toCloudFormation(stack) as SynthedStack; - - expect(json.Parameters.Stage).toEqual({ - Type: "String", - Description: "Stage name", - AllowedValues: Stages, - Default: Stage.CODE, - }); - }); -}); - -describe("The GuStackParameter class", () => { - it("should set the values as required", () => { - const stack = new Stack() as GuStack; - - new GuStackParameter(stack); - - const json = SynthUtils.toCloudFormation(stack) as SynthedStack; - - expect(json.Parameters.Stack).toEqual({ - Type: "String", - Description: "Name of this stack", - Default: "deploy", - }); - }); -}); - describe("The GuInstanceTypeParameter class", () => { it("should combine default, override and prop values", () => { const stack = simpleGuStackForTesting(); From 40292cba87edbf3b94df57448d2b95b65e4b0db4 Mon Sep 17 00:00:00 2001 From: Akash Askoolum Date: Fri, 12 Feb 2021 16:50:18 +0000 Subject: [PATCH 3/3] remove stack and stage private fields from GuStack These can be replaced with the new getParam helper. --- src/constructs/core/parameters.ts | 6 ++++-- src/constructs/core/stack.ts | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/constructs/core/parameters.ts b/src/constructs/core/parameters.ts index ba93880dd6..63b0a7ba11 100644 --- a/src/constructs/core/parameters.ts +++ b/src/constructs/core/parameters.ts @@ -30,7 +30,8 @@ export class GuStringParameter extends GuParameter { } export class GuStageParameter extends GuParameter { - constructor(scope: GuStack, id: string = "Stage") { + public static readonly defaultId = "Stage"; + constructor(scope: GuStack, id: string = GuStageParameter.defaultId) { super(scope, id, { type: "String", description: "Stage name", @@ -41,7 +42,8 @@ export class GuStageParameter extends GuParameter { } export class GuStackParameter extends GuParameter { - constructor(scope: GuStack, id: string = "Stack") { + public static readonly defaultId = "Stack"; + constructor(scope: GuStack, id: string = GuStackParameter.defaultId) { super(scope, id, { type: "String", description: "Name of this stack", diff --git a/src/constructs/core/stack.ts b/src/constructs/core/stack.ts index df1a97f031..d3d011ac1c 100644 --- a/src/constructs/core/stack.ts +++ b/src/constructs/core/stack.ts @@ -40,7 +40,6 @@ export interface GuStackProps extends StackProps { * ``` */ export class GuStack extends Stack { - private readonly _stage: GuStageParameter; private readonly _stack: string; private readonly _app: string; @@ -49,7 +48,7 @@ export class GuStack extends Stack { public readonly migratedFromCloudFormation: boolean; get stage(): string { - return this._stage.valueAsString; + return this.getParam(GuStageParameter.defaultId).valueAsString; } get stack(): string { @@ -96,7 +95,7 @@ export class GuStack extends Stack { this.params = new Map(); - this._stage = new GuStageParameter(this); + new GuStageParameter(this); this._stack = props.stack; this._app = props.app;