Repository: alexcasalboni/aws-lambda-power-tuning (js)

Issue:
Payload as a simple string
How to pass string payload for tuner for Lambda function declared as

  public string FunctionHandler(string input, ILambdaContext context)

I tried these
1. "payload": "test data", - It doesn't work. Error lambda invocation
2. "payload" : {} -  It doesn't work. Error lambda invocation
3.  did not pass payload parameter - It doesn't work. Error lambda invocation

Please provide examples of sample payload

Pull request description:
Bugfix: string payload
Fix #148.


Patch:
diff --git a/.gitignore b/.gitignore
index 4ceb14f..5c3e849 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,7 +18,3 @@ serverless.yml
 .vscode/
 
 layer-sdk/src
-
-# ignore SAM CLI created files/dirs
-.aws-sam/
-samconfig.toml
diff --git a/.travis.yml b/.travis.yml
index 82e85b3..6a0a5fa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,11 @@
 language: node_js
 sudo: false
 node_js:
+  - 10.13
+  - 11
   - 12
   - 13
   - 14
-  - 15
-  - 16
 install:
   # install Node.js dependencies
   - travis_retry npm ci
diff --git a/README-DEPLOY.md b/README-DEPLOY.md
index 210c7b5..9d8f042 100644
--- a/README-DEPLOY.md
+++ b/README-DEPLOY.md
@@ -1,122 +1,132 @@
-# How to deploy the AWS Lambda Power Tuning tool
+# How to deploy the state machine
 
-There are multiple options to deploy the tool.
+There are multiple ways to deploy the project.
 
-If you are familiar with Infrastructure as Code, there are 4 ways for you to create all of the resources neccesary for Lambda Power Tuning.
+If you are familiar with Infrastructure as Code, the easiest way is to deploy the app via the Serverless Application Repository (SAR) - see option 1 and 3. If you prefer Terraform, see option 6.
 
-The following three options utilize [AWS CloudFormation](https://aws.amazon.com/cloudformation/) on your behalf to create the neccessary resources. Each will create a new CloudFormation stack in your AWS account containing all the resources for the Lambda Power Tuning tool.
-1. The easiest way is to [deploy the app via the AWS Serverless Application Repository (SAR)](#option1)
-1. Manually [using the AWS SAM CLI](#option2)
-1. Manually [using the AWS CDK](#option3)
+If you prefer to verify and own the full YAML template, feel free to fork this repo and deploy everything with AWS SAM CLI - see option 2.
 
-You can also [deploy manually with Terraform](#option6) by Hashicorp.
+If you don't want to deal with CloudFormation or SAR at all, you can use the Lumigo CLI (which will take care of both deployment and execution) or the Lambda Power Tuner UI - see option 4 and 5.
 
-If you don't want to deal with any Infrastructure as Code tool, you can use one of the following:
-1. The [Lumigo CLI](#option4) (which will take care of both deployment and execution)
-1.  The [Lambda Power Tuner UI](#option5) 
 
-
-## Option 1: AWS Serverless Application Repository<a name="option1"></a>
+## Option 1: AWS Serverless Application Repository
 
 You can find this app in the [Serverless Application Repository](https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:451282441545:applications~aws-lambda-power-tuning) and deploy it with just a few clicks in the AWS Management Console.
 
 You can also integrate the SAR app in your existing CloudFormation stacks - check [scripts/deploy-sar-app.yml](scripts/deploy-sar-app.yml) and [scripts/deploy-sar-app.sh](scripts/deploy-sar-app.sh) for a sample implementation.
 
 
-## Option 2: Build and deploy with the AWS SAM CLI<a name="option2"></a>
-
-1. Install the [AWS SAM CLI in your local environment](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html).
-
-1. Configure your [AWS credentials (requires AWS CLI installed)](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration):
-    ```bash
-    $ aws configure
-    ```
-1. Clone this git repository: 
-    ```bash
-    $ git clone https://github.com/alexcasalboni/aws-lambda-power-tuning.git
-    ```
-1. Build the Lambda layer and any other dependencies:
-    ```bash
-    $ cd ./aws-lambda-power-tuning
-    $ sam build -u
-    ```
-    [`sam build -u`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-build.html) will run SAM build using a Docker container image that provides an environment similar to that which your function would run in. SAM build in-turn looks at your AWS SAM template file for information about Lambda functions and layers in this project.
-    
-    Once the build has completed you should see output that states `Build Succeeded`. If not there will be error messages providing guidance on what went wrong.
-1.  Deploy the applicaiton using the SAM deploy "guided" mode:
-    ```bash
-    $ sam deploy -g
-    ```
-    [`sam deploy -g`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html) will provide simple prompts to walk you through the process of deploying the tool. Provide a unique name for the 'Stack Name' and supply the [AWS Region](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html#Concepts.RegionsAndAvailabilityZones.Regions) you want to run the tool in and then you can select the defaults for testing of this tool. After accepting the promted questions with a "Y" you can optionally save your application configuration. 
-
-    After that the SAM CLI will run the required commands to create the resources for the Lambda Power Tuning tool. The CloudFormation outputs shown will highlight any issues or failures.
-    
-    If there are no issues, once complete you will see the stack ouputs and a `Successfully created/updated stack` message.
-  
-
-## Option 3: Deploy the AWS SAR app with AWS CDK<a name="option3"></a>
-
-1. [Install AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) and [configure your AWS credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration):
-
-    ```bash
-    $ npm install -g aws-cdk
-    $ aws configure
-    ```
-
-1. If you already have a CDK project you can include the following to use the [sam module](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-sam-readme.html):
-
-    ```typescript
-    import sam = require('@aws-cdk/aws-sam');
-    
-    new sam.CfnApplication(this, 'powerTuner', {
-      location: {
-        applicationId: 'arn:aws:serverlessrepo:us-east-1:451282441545:applications/aws-lambda-power-tuning',
-        semanticVersion: '4.1.2'
-      },
-      parameters: {
-        "lambdaResource": "*",
-        "PowerValues": "128,256,512,1024,1536,3008"
-      }
-    })
-    ```
-
-    Alternatively, you can use [CDK Patterns](https://github.com/cdk-patterns/serverless) to give you a pre configured project in either TypeScript or Python:
-    
-    ```bash
-    # For the TypeScript CDK version
-    npx cdkp init the-lambda-power-tuner
-    
-    # or for the Python CDK version
-    npx cdkp init the-lambda-power-tuner --lang=python
-    ```
-
-1. To deploy the TypeScript version you just need to:
-
-    ```bash
-    cd the-lambda-power-tuner
-    npm run deploy
-    ```
-
-    For Python deployment, see the instructions [here](https://github.com/cdk-patterns/serverless#2-download-pattern-in-python-or-typescript-cdk).
-
-## Option 4: Deploy with the Lumigo CLI<a name="option4"></a>
-
-1. Install the Lumigo CLI:
-    ```bash
-    $ npm install -g lumigo-cli
-    ```
-1. Power-tune your functions as follows:
-    ```bash
-    $ lumigo-cli powertune-lambda <OPTIONS>
-    ```
+## Option 2: fork this repo and deploy with AWS SAM
+
+In case you want to deploy it "manually", you can run [scripts/deploy.sh](scripts/deploy.sh).
+
+The script uses the [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) to create a new CloudFormation stack in your account.
+
+First, install AWS SAM and [configure your AWS credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration):
+
+
+```bash
+$ pip install aws-sam-cli
+$ aws configure
+```
+
+Now, you can clone this repository as follows:
+
+```bash
+$ git clone https://github.com/alexcasalboni/aws-lambda-power-tuning.git
+```
+
+Configure your deployment bucket name ([create one first!](https://docs.aws.amazon.com/AmazonS3/latest/user-guide/create-bucket.html)) and stack name in `scripts/deploy.sh`:
+
+
+```bash
+# config
+BUCKET_NAME=your-sam-templates-bucket
+STACK_NAME=lambda-power-tuning
+# more config parameters are available too!
+```
+
+Build the SDK layer as follows:
+
+```bash
+$ bash scripts/build-layer.sh
+```
+
+You can finally deploy the serverless app:
+
+```bash
+$ bash scripts/deploy.sh
+```
+
+## Option 3: deploy the SAR app with AWS CDK
+
+First, [install AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) and [configure your AWS credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration):
+
+```bash
+$ pip install aws-sam-cli
+$ aws configure
+```
+
+If you already have a CDK project you can include the following to use the [sam module](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-sam-readme.html):
+
+```typescript
+import sam = require('@aws-cdk/aws-sam');
+
+new sam.CfnApplication(this, 'powerTuner', {
+  location: {
+    applicationId: 'arn:aws:serverlessrepo:us-east-1:451282441545:applications/aws-lambda-power-tuning',
+    semanticVersion: '4.1.2'
+  },
+  parameters: {
+    "lambdaResource": "*",
+    "PowerValues": "128,256,512,1024,1536,3008"
+  }
+})
+```
+
+Alternatively you can use [CDK Patterns](https://github.com/cdk-patterns/serverless) to give you a pre configured project in either TypeScript or Python:
+
+```bash
+# For the TypeScript CDK version
+npx cdkp init the-lambda-power-tuner
+
+# or for the Python CDK version
+npx cdkp init the-lambda-power-tuner --lang=python
+```
+
+To deploy the TypeScript version you just need to:
+
+```bash
+cd the-lambda-power-tuner
+npm run deploy
+```
+
+For Python deployment, see the instructions [here](https://github.com/cdk-patterns/serverless#2-download-pattern-in-python-or-typescript-cdk).
+
+## Option 4: deploy with the Lumigo CLI
+
+You can install the Lumigo CLI:
+
+```bash
+$ npm install -g lumigo-cli
+```
+
+And then power-tune your functions as follows:
+
+```bash
+$ lumigo-cli powertune-lambda <OPTIONS>
+```
 
 For the full documentation of the command parameters:
+
 ```bash
 $ lumigo-cli --help powertune-lambda
 ```
+
 (or check it out [here](https://www.npmjs.com/package/lumigo-cli#lumigo-cli-powertune-lambda)).
 
-## Option 5: Deploy via AWS Lambda Power Tuner UI<a name="option5"></a>
+
+## Option 5: deploy via AWS Lambda Power Tuner UI
 
 You can deploy and interact with Lambda Power Tuning with an ad-hoc web interface. This UI will deploy everything you need to power-tune your functions and also simplify the input/output management for Step Functions via API Gateway.
 
@@ -124,7 +134,7 @@ You can find the open-source project and the instructions to deploy it here: [ma
 
 ![Power Tuner UI](https://github.com/mattymoomoo/aws-power-tuner-ui/blob/master/imgs/website.png?raw=true)
 
-## Option 6: Deploy the SAR app with Terraform<a name="option6"></a>
+## Option 6: deploy the SAR app with Terraform
 
 Simply add the `aws_serverlessapplicationrepository_cloudformation_stack` resource below to your Terraform code and deploy as usual through `terraform apply`.
 
diff --git a/lambda/utils.js b/lambda/utils.js
index 40573de..7ecbe73 100644
--- a/lambda/utils.js
+++ b/lambda/utils.js
@@ -333,9 +333,26 @@ module.exports.generatePayloads = (num, payloadInput) => {
  * Convert payload to string, if it's not a string already
  */
 module.exports.convertPayload = (payload) => {
+    /**
+     * Return true only if the input is a JSON-encoded string.
+     * For example, '"test"' or '{"key": "value"}'.
+     */
+    const isJsonString = (s) => {
+        if (typeof s !== 'string')
+            return false;
+
+        try {
+            JSON.parse(s);
+        } catch (e) {
+            return false;
+        }
+        return true;
+    };
+
     // optionally convert everything into string
-    if (typeof payload !== 'string' && typeof payload !== 'undefined') {
-        console.log('Converting payload to string from ', typeof payload);
+    if (typeof payload !== 'undefined' && !isJsonString(payload)) {
+        // note: 'just a string' becomes '"just a string"'
+        console.log('Converting payload to JSON string from ', typeof payload);
         payload = JSON.stringify(payload);
     }
     return payload;
diff --git a/scripts/build-layer.sh b/scripts/build-layer.sh
new file mode 100644
index 0000000..72178d4
--- /dev/null
+++ b/scripts/build-layer.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# make sure we're working on the layer folder
+cd layer-sdk
+
+# create subfolders
+## ./src is referenced by the LayerVersion resource (.gitignored)
+## ./src/nodejs will contain the node_modules
+mkdir -p ./src/nodejs
+
+# install layer dependencies (the SDK)
+npm i
+
+# clean up previous build ...
+rm -rf ./src/nodejs/node_modules
+
+# ... and move everything into the layer sub-folder
+mv ./node_modules ./src/nodejs
\ No newline at end of file
diff --git a/scripts/deploy.sh b/scripts/deploy.sh
new file mode 100755
index 0000000..a14925a
--- /dev/null
+++ b/scripts/deploy.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# config
+DEFAULT_BUCKET_NAME=your-sam-templates-bucket
+DEFAULT_STACK_NAME=lambda-power-tuning
+
+BUCKET_NAME=${BUCKET_NAME:-$DEFAULT_BUCKET_NAME}
+STACK_NAME=${STACK_NAME:-$DEFAULT_STACK_NAME}
+PowerValues='128,256,512,1024,1536,3008'
+LambdaResource='*'
+TotalExecutionTimeout="300"
+
+# package
+sam package --s3-bucket $BUCKET_NAME --template-file template.yml --output-template-file packaged.yml
+
+# deploy
+sam deploy --template-file packaged.yml --stack-name $STACK_NAME --capabilities CAPABILITY_IAM --parameter-overrides PowerValues=$PowerValues lambdaResource=$LambdaResource totalExecutionTimeout=$TotalExecutionTimeout
diff --git a/template.yml b/template.yml
index 2369932..bceb388 100644
--- a/template.yml
+++ b/template.yml
@@ -1,6 +1,5 @@
 AWSTemplateFormatVersion: '2010-09-09'
 Transform: AWS::Serverless-2016-10-31
-Description: AWS Lambda Power Tuning resources.
 
 Metadata:
   AWS::ServerlessRepo::Application:
@@ -65,13 +64,11 @@ Resources:
     Properties:
       LayerName: AWS-SDK-v2_989_0
       Description: AWS SDK 2.989.0
-      ContentUri: ./layer-sdk
+      ContentUri: ./layer-sdk/src
       CompatibleRuntimes:
         - nodejs14.x
       LicenseInfo: 'Available under the MIT-0 license.'
       RetentionPolicy: Retain
-    Metadata:
-      BuildMethod: nodejs14.x
 
   initializer:
     Type: AWS::Serverless::Function


Test patch:
diff --git a/test/unit/test-utils.js b/test/unit/test-utils.js
index b8f14d5..9e76c45 100644
--- a/test/unit/test-utils.js
+++ b/test/unit/test-utils.js
@@ -426,21 +426,39 @@ describe('Lambda Utils', () => {
         });
     });
 
+    const isJsonString = (str) => {
+        try {
+            JSON.parse(str);
+        } catch (e) {
+            return false;
+        }
+        return true;
+    };
+
     describe('convertPayload', () => {
 
-        it('should return the same string when a string is given', async() => {
+        it('should JSON-encode strings, if not JSON strings already', async() => {
             const strings = [
                 'test',
+                '',
+                ' ',
+            ];
+            strings.forEach(s => {
+                expect(utils.convertPayload(s)).to.be('"' + s + '"');
+                expect(isJsonString(utils.convertPayload(s))).to.be(true);
+            });
+        });
+
+        it('should return already a JSON-encoded string as is', async() => {
+            const strings = [
                 '{"test": true}',
                 '[]',
-                'undefined',
                 'true',
                 'null',
-                '',
-                ' ',
             ];
             strings.forEach(s => {
                 expect(utils.convertPayload(s)).to.be(s);
+                expect(isJsonString(utils.convertPayload(s))).to.be(true);
             });
         });
 
@@ -450,6 +468,7 @@ describe('Lambda Utils', () => {
         });
 
         it('should convert everything else to string', async() => {
+            expect(utils.convertPayload(null)).to.be('null');
             expect(utils.convertPayload({})).to.be('{}');
             expect(utils.convertPayload({test: true})).to.be('{"test":true}');
             expect(utils.convertPayload([])).to.be('[]');
@@ -467,6 +486,18 @@ describe('Lambda Utils', () => {
             expect(output.length).to.be(10);
             output.forEach(p => {
                 expect(p).to.be('{"test":true}');
+                expect(isJsonString(p)).to.be(true);
+            });
+        });
+
+        it('should generate a list of encoded JSON strings, if not weighted', async() => {
+            const payload = 'just a string';
+
+            const output = utils.generatePayloads(10, payload);
+            expect(output.length).to.be(10);
+            output.forEach(p => {
+                expect(p).to.be('"just a string"');
+                expect(isJsonString(p)).to.be(true);
             });
         });