From 71cb421d3b38bcc3f1fbfad3028b25df5fd2cb77 Mon Sep 17 00:00:00 2001
From: Romain Marcadier-Muller <rmuller@amazon.com>
Date: Mon, 1 Jul 2019 15:31:54 +0200
Subject: [PATCH] fix(code): make CfnResource#_toCloudFormation null-safe
 (#3121)

The `CfnResource#_toCloudFormation` method creates a `PostResolveToken`
with a post-processor that was not ready to handle the absence of
`Properties` on the resolved value. It is however possible that
`Properties` are missing when an object is created with the default
configuration (e.g: by `new sqs.CfnQueue(this, 'Queue');`).

This change makes the post-processor function correctly handle
`undefined` in this case.

Related #3093
---
 packages/@aws-cdk/core/lib/cfn-resource.ts    |  3 +-
 .../@aws-cdk/core/test/test.cfn-resource.ts   | 29 +++++++++++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)
 create mode 100644 packages/@aws-cdk/core/test/test.cfn-resource.ts

diff --git a/packages/@aws-cdk/core/lib/cfn-resource.ts b/packages/@aws-cdk/core/lib/cfn-resource.ts
index edb7d3f87a8ea..31dc062f5ce43 100644
--- a/packages/@aws-cdk/core/lib/cfn-resource.ts
+++ b/packages/@aws-cdk/core/lib/cfn-resource.ts
@@ -230,7 +230,8 @@ export class CfnResource extends CfnRefElement {
             Metadata: ignoreEmpty(this.cfnOptions.metadata),
             Condition: this.cfnOptions.condition && this.cfnOptions.condition.logicalId
           }, props => {
-            props.Properties = this.renderProperties(props.Properties);
+            const renderedProps = this.renderProperties(props.Properties || {});
+            props.Properties = renderedProps && (Object.values(renderedProps).find(v => !!v) ? renderedProps : undefined);
             return deepMerge(props, this.rawOverrides);
           })
         }
diff --git a/packages/@aws-cdk/core/test/test.cfn-resource.ts b/packages/@aws-cdk/core/test/test.cfn-resource.ts
new file mode 100644
index 0000000000000..db83634ecccd3
--- /dev/null
+++ b/packages/@aws-cdk/core/test/test.cfn-resource.ts
@@ -0,0 +1,29 @@
+import nodeunit = require('nodeunit');
+import core = require('../lib');
+
+export = nodeunit.testCase({
+  '._toCloudFormation': {
+    'does not call renderProperties with an undefined value'(test: nodeunit.Test) {
+      const app = new core.App();
+      const stack = new core.Stack(app, 'TestStack');
+      const resource = new core.CfnResource(stack, 'DefaultResource', { type: 'Test::Resource::Fake' });
+
+      let called = false;
+      (resource as any).renderProperties = (val: any) => {
+        called = true;
+        test.notEqual(val, null);
+      };
+
+      test.deepEqual(app.synth().getStack(stack.stackName).template, {
+        Resources: {
+          DefaultResource: {
+            Type: 'Test::Resource::Fake'
+          }
+        }
+      });
+      test.ok(called, `renderProperties must be called called`);
+
+      test.done();
+    }
+  }
+});