From 38868d05a70149b699916552ee4e00ff04bd9748 Mon Sep 17 00:00:00 2001 From: comcalvi <66279577+comcalvi@users.noreply.github.com> Date: Fri, 31 Jul 2020 18:11:19 -0400 Subject: [PATCH] feat(core): make the CfnParameter class mutable (#9365) Closes #9364 ---- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license --- .../@aws-cdk/cloudformation-include/README.md | 18 ++ .../test/valid-templates.test.ts | 46 +++++ packages/@aws-cdk/core/lib/cfn-parameter.ts | 186 ++++++++++++++++-- 3 files changed, 236 insertions(+), 14 deletions(-) diff --git a/packages/@aws-cdk/cloudformation-include/README.md b/packages/@aws-cdk/cloudformation-include/README.md index 0e08b7f5fb745..535846de608fb 100644 --- a/packages/@aws-cdk/cloudformation-include/README.md +++ b/packages/@aws-cdk/cloudformation-include/README.md @@ -110,6 +110,24 @@ Note that [Custom Resources](https://docs.aws.amazon.com/AWSCloudFormation/lates will be of type CfnResource, and hence won't need to be casted. This holds for any resource that isn't in the CloudFormation schema. +## Parameters + +If your template uses [CloudFormation Parameters] (https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html), +you can retrieve them from your template: + +```typescript +import * as core from '@aws-cdk/core'; + +const param: core.CfnParameter = cfnTemplate.getParameter('MyParameter'); +``` + +The `CfnParameter` object is mutable, +and any changes you make to it will be reflected in the resulting template: + +```typescript +param.default = 'MyDefault'; +``` + ## Conditions If your template uses [CloudFormation Conditions](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html), diff --git a/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts b/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts index 07529248be53b..329cda9e759c8 100644 --- a/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts @@ -397,6 +397,52 @@ describe('CDK Include', () => { }).toThrow(/Parameter with name 'FakeBucketNameThatDoesNotExist' was not found in the template/); }); + test('reflects changes to a retrieved CfnParameter object in the resulting template', () => { + const cfnTemplate = includeTestTemplate(stack, 'bucket-with-parameters.json'); + const stringParam = cfnTemplate.getParameter('BucketName'); + const numberParam = cfnTemplate.getParameter('CorsMaxAge'); + + stringParam.default = 'MyDefault'; + stringParam.allowedPattern = '[0-9]*$'; + stringParam.allowedValues = ['123123', '456789']; + stringParam.constraintDescription = 'MyNewConstraint'; + stringParam.description = 'a string of numeric characters'; + stringParam.maxLength = 6; + stringParam.minLength = 2; + + numberParam.maxValue = 100; + numberParam.minValue = 4; + numberParam.noEcho = false; + numberParam.type = "NewType"; + const originalTemplate = loadTestFileToJsObject('bucket-with-parameters.json'); + + expect(stack).toMatchTemplate({ + "Resources": { + ...originalTemplate.Resources, + }, + "Parameters": { + ...originalTemplate.Parameters, + "BucketName": { + ...originalTemplate.Parameters.BucketName, + "Default": "MyDefault", + "AllowedPattern": "[0-9]*$", + "AllowedValues": [ "123123", "456789" ], + "ConstraintDescription": "MyNewConstraint", + "Description": "a string of numeric characters", + "MaxLength": 6, + "MinLength": 2, + }, + "CorsMaxAge": { + ...originalTemplate.Parameters.CorsMaxAge, + "MaxValue": 100, + "MinValue": 4, + "NoEcho": false, + "Type": "NewType", + }, + }, + }); + }); + test('reflects changes to a retrieved CfnCondition object in the resulting template', () => { const cfnTemplate = includeTestTemplate(stack, 'resource-attribute-condition.json'); const alwaysFalseCondition = cfnTemplate.getCondition('AlwaysFalseCond'); diff --git a/packages/@aws-cdk/core/lib/cfn-parameter.ts b/packages/@aws-cdk/core/lib/cfn-parameter.ts index 8d91f7c006777..6226f451c2fb8 100644 --- a/packages/@aws-cdk/core/lib/cfn-parameter.ts +++ b/packages/@aws-cdk/core/lib/cfn-parameter.ts @@ -97,7 +97,17 @@ export interface CfnParameterProps { * update a stack. */ export class CfnParameter extends CfnElement { - private readonly type: string; + private _type: string; + private _default?: any; + private _allowedPattern?: string; + private _allowedValues?: string[]; + private _constraintDescription?: string; + private _description?: string; + private _maxLength?: number; + private _maxValue?: number; + private _minLength?: number; + private _minValue?: number; + private _noEcho?: boolean; /** * Creates a parameter construct. @@ -107,17 +117,165 @@ export class CfnParameter extends CfnElement { * @param scope The parent construct. * @param props The parameter properties. */ - constructor(scope: Construct, id: string, private readonly props: CfnParameterProps = {}) { + constructor(scope: Construct, id: string, props: CfnParameterProps = {}) { super(scope, id); - this.type = props.type || 'String'; + this._type = props.type || 'String'; + this._default = props.default; + this._allowedPattern = props.allowedPattern; + this._allowedValues = props.allowedValues; + this._constraintDescription = props.constraintDescription; + this._description = props.description; + this._maxLength = props.maxLength; + this._maxValue = props.maxValue; + this._minLength = props.minLength; + this._minValue = props.minValue; + this._noEcho = props.noEcho; + } + + /** + * The data type for the parameter (DataType). + * + * @default String + */ + public get type(): string { + return this._type; + } + + public set type(type: string) { + this._type = type; + } + + /** + * A value of the appropriate type for the template to use if no value is specified + * when a stack is created. If you define constraints for the parameter, you must specify + * a value that adheres to those constraints. + * + * @default - No default value for parameter. + */ + public get default(): any { + return this._default; + } + + public set default(value: any) { + this._default = value; + } + + /** + * A regular expression that represents the patterns to allow for String types. + * + * @default - No constraints on patterns allowed for parameter. + */ + public get allowedPattern(): string | undefined { + return this._allowedPattern; + } + + public set allowedPattern(pattern: string | undefined) { + this._allowedPattern = pattern; + } + + /** + * An array containing the list of values allowed for the parameter. + * + * @default - No constraints on values allowed for parameter. + */ + public get allowedValues(): string[] | undefined { + return this._allowedValues; + } + + public set allowedValues(values: string[] | undefined) { + this._allowedValues = values; + } + + /** + * A string that explains a constraint when the constraint is violated. + * For example, without a constraint description, a parameter that has an allowed + * pattern of [A-Za-z0-9]+ displays the following error message when the user specifies + * an invalid value: + * + * @default - No description with customized error message when user specifies invalid values. + */ + public get constraintDescription(): string | undefined { + return this._constraintDescription; + } + + public set constraintDescription(desc: string | undefined) { + this._constraintDescription = desc; + } + + /** + * A string of up to 4000 characters that describes the parameter. + * + * @default - No description for the parameter. + */ + public get description(): string | undefined { + return this._description; + } + + public set description(desc: string | undefined) { + this._description = desc; + } + + /** + * An integer value that determines the largest number of characters you want to allow for String types. + * + * @default - None. + */ + public get maxLength(): number | undefined { + return this._maxLength; + } + + public set maxLength(len: number | undefined) { + this._maxLength = len; + } + + /** + * An integer value that determines the smallest number of characters you want to allow for String types. + * + * @default - None. + */ + public get minLength(): number | undefined { + return this._minLength; + } + + public set minLength(len: number | undefined) { + this._minLength = len; + } + + /** + * A numeric value that determines the largest numeric value you want to allow for Number types. + * + * @default - None. + */ + public get maxValue(): number | undefined { + return this._maxValue; + } + + public set maxValue(len: number | undefined) { + this._maxValue = len; + } + /** + * A numeric value that determines the smallest numeric value you want to allow for Number types. + * + * @default - None. + */ + public get minValue(): number | undefined { + return this._minValue; + } + + public set minValue(len: number | undefined) { + this._minValue = len; } /** * Indicates if this parameter is configured with "NoEcho" enabled. */ public get noEcho(): boolean { - return !!this.props.noEcho; + return !!this._noEcho; + } + + public set noEcho(echo: boolean) { + this._noEcho = echo; } /** @@ -165,16 +323,16 @@ export class CfnParameter extends CfnElement { Parameters: { [this.logicalId]: { Type: this.type, - Default: this.props.default, - AllowedPattern: this.props.allowedPattern, - AllowedValues: this.props.allowedValues, - ConstraintDescription: this.props.constraintDescription, - Description: this.props.description, - MaxLength: this.props.maxLength, - MaxValue: this.props.maxValue, - MinLength: this.props.minLength, - MinValue: this.props.minValue, - NoEcho: this.props.noEcho, + Default: this.default, + AllowedPattern: this.allowedPattern, + AllowedValues: this.allowedValues, + ConstraintDescription: this.constraintDescription, + Description: this.description, + MaxLength: this.maxLength, + MaxValue: this.maxValue, + MinLength: this.minLength, + MinValue: this.minValue, + NoEcho: this._noEcho, }, }, };