Repository: serverless/serverless (JavaScript)
Date: 2017-01-14 06:44:52+00:00
Task type: Bug Fix

Issue:
Multi-line values in IoT event
<!--
1. If you have a question and not a bug/feature request please ask it at http://forum.serverless.com
2. Please check if an issue already exists so there are no duplicates
3. Check out and follow our Guidelines: https://github.com/serverless/serverless/blob/master/CONTRIBUTING.md
4. Fill out the whole template so we have a good overview on the issue
5. Do not remove any section of the template. If something is not applicable leave it empty but leave it in the Issue
6. Please follow the template, otherwise we'll have to ask you to update it
-->

# This is a Bug Report

Using multi-line values (with yaml >) in an IoT definition results in an 'unexpected token' error.

For bug reports:
* What went wrong?  The following yaml:
```
events:
      - iot:
          description: >
            My multi-
            line value.
          name: enrollPlayer
          sql: >
            SELECT * FROM 'path/#'
            WHERE prop = 'value'
```
complain of an "Unexpected token" when deployed.   Looking at the value of `iotTemplate` in file `serverless/lib/plugins/aws/deploy/compile/events/iot/index.js`, it seems the trailing linefeed is being preserved:
```
           "Description": "My multi- line value.
            ",
            "RuleDisabled": "false",
            "Sql": "SELECT * FROM 'path/#' WHERE prop = 'value'
            ",
```
* What did you expect should have happened?  The multi-line input should not break the ending quote on the next line.
* What was the config you used?
* What stacktrace or error message from your provider did you see?

Similar or dependent issues:
*

* ***Serverless Framework Version you're using***: 1.5.0
* ***Operating System***: OS X 10.12
* ***Stack Trace***:
SyntaxError: Unexpected token 

    at Object.parse (native)
    at /usr/local/lib/node_modules/serverless/lib/plugins/aws/deploy/compile/events/iot/index.js:99:36
    at Array.forEach (native)
    at /usr/local/lib/node_modules/serverless/lib/plugins/aws/deploy/compile/events/iot/index.js:21:28
    at Array.forEach (native)
    at AwsCompileIoTEvents.compileIoTEvents (/usr/local/lib/node_modules/serverless/lib/plugins/aws/deploy/compile/events/iot/index.js:16:47)
    at /usr/local/lib/node_modules/serverless/lib/classes/PluginManager.js:160:50
    at tryCatcher (/usr/local/lib/node_modules/serverless/node_modules/bluebird/js/release/util.js:16:23)
    at Object.gotValue (/usr/local/lib/node_modules/serverless/node_modules/bluebird/js/release/reduce.js:157:18)
    at Object.gotAccum (/usr/local/lib/node_modules/serverless/node_modules/bluebird/js/release/reduce.js:144:25)
    at Object.tryCatcher (/usr/local/lib/node_modules/serverless/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/usr/local/lib/node_modules/serverless/node_modules/bluebird/js/release/promise.js:510:31)
    at Promise._settlePromise (/usr/local/lib/node_modules/serverless/node_modules/bluebird/js/release/promise.js:567:18)
    at Promise._settlePromise0 (/usr/local/lib/node_modules/serverless/node_modules/bluebird/js/release/promise.js:612:10)
    at Promise._settlePromises (/usr/local/lib/node_modules/serverless/node_modules/bluebird/js/release/promise.js:691:18)
    at Async._drainQueue (/usr/local/lib/node_modules/serverless/node_modules/bluebird/js/release/async.js:138:16)
* ***Provider Error messages***:
  Syntax Error -------------------------------------------
 
     Unexpected token 



Issue discussion:
it's reproducible on my machine, will fix and send PR.

Solution patch:
diff --git a/lib/plugins/aws/deploy/compile/events/iot/index.js b/lib/plugins/aws/deploy/compile/events/iot/index.js
index 0d6b01f0740..2c56cedd06b 100644
--- a/lib/plugins/aws/deploy/compile/events/iot/index.js
+++ b/lib/plugins/aws/deploy/compile/events/iot/index.js
@@ -55,12 +55,13 @@ class AwsCompileIoTEvents {
               {
                 "Type": "AWS::IoT::TopicRule",
                 "Properties": {
-                  ${RuleName ? `"RuleName": "${RuleName}",` : ''}
+                  ${RuleName ? `"RuleName": "${RuleName.replace(/\r?\n/g, '')}",` : ''}
                   "TopicRulePayload": {
-                    ${AwsIotSqlVersion ? `"AwsIotSqlVersion": "${AwsIotSqlVersion}",` : ''}
-                    ${Description ? `"Description": "${Description}",` : ''}
+                    ${AwsIotSqlVersion ? `"AwsIotSqlVersion":
+                      "${AwsIotSqlVersion.replace(/\r?\n/g, '')}",` : ''}
+                    ${Description ? `"Description": "${Description.replace(/\r?\n/g, '')}",` : ''}
                     "RuleDisabled": "${RuleDisabled}",
-                    "Sql": "${Sql}",
+                    "Sql": "${Sql.replace(/\r?\n/g, '')}",
                     "Actions": [
                       {
                         "Lambda": {


Test patch:
diff --git a/lib/plugins/aws/deploy/compile/events/iot/index.test.js b/lib/plugins/aws/deploy/compile/events/iot/index.test.js
index aa345a7944e..44e9c756139 100644
--- a/lib/plugins/aws/deploy/compile/events/iot/index.test.js
+++ b/lib/plugins/aws/deploy/compile/events/iot/index.test.js
@@ -95,7 +95,7 @@ describe('AwsCompileIoTEvents', () => {
           events: [
             {
               iot: {
-                name: 'iotEvantName',
+                name: 'iotEventName',
                 sql: "SELECT * FROM 'topic_1'",
               },
             },
@@ -108,7 +108,7 @@ describe('AwsCompileIoTEvents', () => {
       expect(awsCompileIoTEvents.serverless.service
         .provider.compiledCloudFormationTemplate.Resources
         .FirstIotTopicRule1.Properties.RuleName
-      ).to.equal('iotEvantName');
+      ).to.equal('iotEventName');
     });
 
     it('should respect "enabled" variable', () => {
@@ -212,6 +212,41 @@ describe('AwsCompileIoTEvents', () => {
       ).to.equal('false');
     });
 
+    it('should respect variables if multi-line variables is given', () => {
+      awsCompileIoTEvents.serverless.service.functions = {
+        first: {
+          events: [
+            {
+              iot: {
+                description: 'iot event description\n with newline',
+                sql: "SELECT * FROM 'topic_1'\n WHERE value = 2",
+                sqlVersion: 'beta\n',
+                name: 'iotEventName\n',
+              },
+            },
+          ],
+        },
+      };
+
+      awsCompileIoTEvents.compileIoTEvents();
+      expect(awsCompileIoTEvents.serverless.service
+        .provider.compiledCloudFormationTemplate.Resources
+        .FirstIotTopicRule1.Properties.TopicRulePayload.Sql
+      ).to.equal("SELECT * FROM 'topic_1' WHERE value = 2");
+      expect(awsCompileIoTEvents.serverless.service
+        .provider.compiledCloudFormationTemplate.Resources
+        .FirstIotTopicRule1.Properties.TopicRulePayload.AwsIotSqlVersion
+      ).to.equal('beta');
+      expect(awsCompileIoTEvents.serverless.service
+        .provider.compiledCloudFormationTemplate.Resources
+        .FirstIotTopicRule1.Properties.TopicRulePayload.Description
+      ).to.equal('iot event description with newline');
+      expect(awsCompileIoTEvents.serverless.service
+        .provider.compiledCloudFormationTemplate.Resources
+        .FirstIotTopicRule1.Properties.RuleName
+      ).to.equal('iotEventName');
+    });
+
     it('should not create corresponding resources when iot events are not given', () => {
       awsCompileIoTEvents.serverless.service.functions = {
         first: {