diff --git a/packages/@aws-cdk/core/lib/cfn-resource.ts b/packages/@aws-cdk/core/lib/cfn-resource.ts index c899d95b657c8..58e6820e4b5a7 100644 --- a/packages/@aws-cdk/core/lib/cfn-resource.ts +++ b/packages/@aws-cdk/core/lib/cfn-resource.ts @@ -146,6 +146,10 @@ export class CfnResource extends CfnRefElement { * If the override is nested, separate each nested level using a dot (.) in the path parameter. * If there is an array as part of the nesting, specify the index in the path. * + * To include a literal `.` in the property name, prefix with a `\`. In most + * programming languages you will need to write this as `"\\."` because the + * `\` itself will need to be escaped. + * * For example, * ```typescript * addOverride('Properties.GlobalSecondaryIndexes.0.Projection.NonKeyAttributes', ['myattribute']) @@ -177,7 +181,7 @@ export class CfnResource extends CfnRefElement { * @param value - The value. Could be primitive or complex. */ public addOverride(path: string, value: any) { - const parts = path.split('.'); + const parts = splitOnPeriods(path); let curr: any = this.rawOverrides; while (parts.length > 1) { @@ -495,3 +499,25 @@ function deepMerge(target: any, ...sources: any[]) { return target; } + +/** + * Split on periods while processing escape characters \ + */ +function splitOnPeriods(x: string): string[] { + // Build this list in reverse because it's more convenient to get the "current" + // item by doing ret[0] than by ret[ret.length - 1]. + const ret = ['']; + for (let i = 0; i < x.length; i++) { + if (x[i] === '\\' && i + 1 < x.length) { + ret[0] += x[i + 1]; + i++; + } else if (x[i] === '.') { + ret.unshift(''); + } else { + ret[0] += x[i]; + } + } + + ret.reverse(); + return ret; +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/test/resource.test.ts b/packages/@aws-cdk/core/test/resource.test.ts index a559a33658ba2..b883d11f8d752 100644 --- a/packages/@aws-cdk/core/test/resource.test.ts +++ b/packages/@aws-cdk/core/test/resource.test.ts @@ -586,6 +586,39 @@ nodeunitShim({ test.done(); }, + 'addOverride(p, v) will not split on escaped dots'(test: Test) { + // GIVEN + const stack = new Stack(); + const r = new CfnResource(stack, 'MyResource', { type: 'AWS::Resource::Type' }); + + // WHEN + r.addOverride(String.raw`Properties.Hello\.World.Foo\.Bar\.Baz`, 42); + r.addOverride(String.raw`Properties.Single\Back\Slashes`, 42); + r.addOverride(String.raw`Properties.Escaped\\.Back\\.Slashes`, 42); + r.addOverride(String.raw`Properties.DoublyEscaped\\\\Back\\\\Slashes`, 42); + r.addOverride('Properties.EndWith\\', 42); // Raw string cannot end with a backslash + + // THEN + test.deepEqual(toCloudFormation(stack), { + Resources: + { + MyResource: + { + Type: 'AWS::Resource::Type', + Properties: + { + 'Hello.World': { 'Foo.Bar.Baz': 42 }, + 'SingleBackSlashes': 42, + 'Escaped\\': { 'Back\\': { Slashes: 42 } }, + 'DoublyEscaped\\\\Back\\\\Slashes': 42, + 'EndWith\\': 42, + }, + }, + }, + }); + test.done(); + }, + 'addPropertyOverride(pp, v) is a sugar for overriding properties'(test: Test) { // GIVEN const stack = new Stack();