Skip to content

Commit

Permalink
Merge branch 'main' into robertd/lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
robertd authored Jun 3, 2022
2 parents babbd4e + 49b397c commit e05f832
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 0 deletions.
53 changes: 53 additions & 0 deletions packages/@aws-cdk/core/lib/cfn-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,33 @@ export interface ICfnResourceOptions {
metadata?: { [key: string]: any };
}

/**
* Object keys that deepMerge should not consider. Currently these include
* CloudFormation intrinsics
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
*/

const MERGE_EXCLUDE_KEYS: string[] = [
'Ref',
'Fn::Base64',
'Fn::Cidr',
'Fn::FindInMap',
'Fn::GetAtt',
'Fn::GetAZs',
'Fn::ImportValue',
'Fn::Join',
'Fn::Select',
'Fn::Split',
'Fn::Sub',
'Fn::Transform',
'Fn::And',
'Fn::Equals',
'Fn::If',
'Fn::Not',
'Fn::Or',
];

/**
* Merges `source` into `target`, overriding any existing values.
* `null`s will cause a value to be deleted.
Expand All @@ -513,6 +540,32 @@ function deepMerge(target: any, ...sources: any[]) {
// object so we can continue the recursion
if (typeof(target[key]) !== 'object') {
target[key] = {};

/**
* If we have something that looks like:
*
* target: { Type: 'MyResourceType', Properties: { prop1: { Ref: 'Param' } } }
* sources: [ { Properties: { prop1: [ 'Fn::Join': ['-', 'hello', 'world'] ] } } ]
*
* Eventually we will get to the point where we have
*
* target: { prop1: { Ref: 'Param' } }
* sources: [ { prop1: { 'Fn::Join': ['-', 'hello', 'world'] } } ]
*
* We need to recurse 1 more time, but if we do we will end up with
* { prop1: { Ref: 'Param', 'Fn::Join': ['-', 'hello', 'world'] } }
* which is not what we want.
*
* Instead we check to see whether the `target` value (i.e. target.prop1)
* is an object that contains a key that we don't want to recurse on. If it does
* then we essentially drop it and end up with:
*
* { prop1: { 'Fn::Join': ['-', 'hello', 'world'] } }
*/
} else if (Object.keys(target[key]).length === 1) {
if (MERGE_EXCLUDE_KEYS.includes(Object.keys(target[key])[0])) {
target[key] = {};
}
}

deepMerge(target[key], value);
Expand Down
70 changes: 70 additions & 0 deletions packages/@aws-cdk/core/test/resource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,76 @@ describe('resource', () => {

});

test('overrides allow overriding one intrinsic with another', () => {
// GIVEN
const stack = new Stack();

const resource = new CfnResource(stack, 'MyResource', {
type: 'MyResourceType',
properties: {
prop1: Fn.ref('Param'),
},
});

// WHEN
resource.addPropertyOverride('prop1', Fn.join('-', ['hello', Fn.ref('Param')]));
const cfn = toCloudFormation(stack);

// THEN
expect(cfn.Resources.MyResource).toEqual({
Type: 'MyResourceType',
Properties: {
prop1: {
'Fn::Join': [
'-',
[
'hello',
{
Ref: 'Param',
},
],
],
},
},
});
});

test('overrides allow overriding a nested intrinsic', () => {
// GIVEN
const stack = new Stack();

const resource = new CfnResource(stack, 'MyResource', {
type: 'MyResourceType',
properties: {
prop1: Fn.importValue(Fn.sub('${Sub}', { Sub: 'Value' })),
},
});

// WHEN
resource.addPropertyOverride('prop1', Fn.importValue(Fn.join('-', ['abc', Fn.sub('${Sub}', { Sub: 'Value' })])));
const cfn = toCloudFormation(stack);

// THEN
expect(cfn.Resources.MyResource).toEqual({
Type: 'MyResourceType',
Properties: {
prop1: {
'Fn::ImportValue': {
'Fn::Join': [
'-',
[
'abc',
{
'Fn::Sub': ['${Sub}', { Sub: 'Value' }],
},
],
],
},
},
},
});
});

describe('using mutable properties', () => {

test('can be used by derived classes to specify overrides before render()', () => {
Expand Down

0 comments on commit e05f832

Please sign in to comment.