MkFoster / lex-dynamodb

Example of how a serverless app can handle Lex requests and log intent and/or session data to dynamodb.
MIT License
3 stars 1 forks source link

Help with readme, index.js and Lambda Response error. #1

Closed CosmicDevs closed 5 years ago

CosmicDevs commented 5 years ago

Hi Mark,

This looks really useful for my project but I'm struggling to get it working as I'm not clear from the readme what lines in the index.js need updating and in what format.

I've updated lines 6, 8 and 31 with my info but when I invoke the Lambda from my bot I get: "An error has occurred: Invalid Lambda Response: Received error response from Lambda: Handled"

Updated Lambda index.js below if that helps.

Any help would be appreciated.

'use strict';
const aws = require('aws-sdk');
const uuidv4 = require('uuid/v4');
const dynamodb = new aws.DynamoDB();
// Update the Table name below
const tableName = process.env.LexLog;
//const tableName = 'LexLog';
const primaryKey = process.env.LexLogId;
//const primaryKey = 'LexLogId';

/**
 * This function expects Lex events and logs intents and
 * session variables to DynamoDB.
 * 
 * Incoming events are routed based on intent.
 */
exports.handler = (event, context, callback) => {
    try {
        // By default, treat the user request as coming from the America/New_York time zone.
        process.env.TZ = 'America/New_York';
        console.log(`event.bot.name=${event.bot.name}`);

        // Output the event in a JSON format we can easily copy and paste
        // from the Cloudwatch logs to facilitate debugging.
        console.log(JSON.stringify(event, null, 4));

        /**
         * Uncomment this if statement and populate with your Lex bot name, alias and / or version as
         * a sanity check to prevent invoking this Lambda function from an undesired source.
         */
        if (event.bot.name !== 'DearDiary') {
            callback('Invalid Bot Name');
        }

        // Get the intent name.  Intent names should match
        // what you see in the AWS Lex Console
        const intentName = event.currentIntent.name;

        // Dispatch to the correct intent handler
        if (intentName === 'Ping') {
            return ping(event, callback);
        } else if (intentName === 'LogIntent') {
            return logIntent(event, callback);
        }
        // Handle unknown intents
        throw new Error(`Intent with name ${intentName} not supported`);
    } catch (err) {
        callback(err);
    }
};

/**
 * This provides a quick way to check connectivity
 * between Lex and this function.
 */
function ping(event, callback) {
    let callbackObj = {
        dialogAction: {
            type: 'Close',
            fulfillmentState: "Fulfilled",
            message: {
                contentType: "PlainText",
                content: "Pong!"
            }
        }
    };
    callback(null, callbackObj);
}

/**
 * This is to hanlde the "LogIntent" intent.
 * Any intent phrases from LogIntent that Lex matches will be
 * logged here.
 * 
 * @param {} event 
 * @param {*} callback 
 */
function logIntent(event, callback) {
    try {
        let docClient = new aws.DynamoDB.DocumentClient({region: process.env.AWS_REGION});

        // Load the data we need to save from the input event into an object
        let item = {
            "currentIntent": event.currentIntent,
            "inputTranscript": event.inputTranscript,
            "requestAttributes": event.requestAttributes,
            "sessionAttributes": event.sessionAttributes
        };
        item[primaryKey] = uuidv4();
        let params = {
            TableName: tableName,
            Item: item
        };

        // Note the data object we are sending to DynamoDB
        console.log(JSON.stringify(params, null, 4));

        console.log('Putting to DynamoDB');
        docClient.put(params, function(err, data) {
            if(err) {
                console.log('Error occurred on DynamoDB put.');
                console.log(err);
                callback(err);
            } else {
                let callbackObj = {
                    dialogAction: {
                        type: 'Close',
                        fulfillmentState: "Fulfilled",
                        message: {
                            contentType: "PlainText",
                            content: "Logged!"
                        }
                    }
                };
                // Save the response we are sending back to Lex in
                // the Cloudwatch logs.
                console.log('Logged!');
                console.log(JSON.stringify(callbackObj, null, 4));
                callback(null, callbackObj);
            }
        });
    } catch (err) {
        callback(err);
    }
}
MkFoster commented 5 years ago

Yah, documentation is a bit weak. Sorry. This was for a hackathon and is meant to be deployed from the serverless app repository which is an option when you choose to create a new lambda function.

That error is probably indicating the Lambda function broke in some way.

The lines you have posted above: "const tableName = process.env.LexLog;" and "const primaryKey = process.env.LexLogId;" won't work because you are referencing environmental variables named "LexLog" and "LexLogId" you probably didn't set in the Lambda console for your function. Either change the lines back the way they were and set the correct environmental variables for "PRIMARY_KEY" and "TABLE_NAME" in your Lambda function's console config or comment out the "process.env" lines entirely and hard code the values like this: const tableName = 'LexLog'; const primaryKey = 'LexLogId'; Assuming "LexLog" is the name of your DynamoDB table and "LexLogId" is the name of your DynamoDB table primary key.

If you are still getting your error, try adding... console.log("got here!") ...to your code to help debug and see how far your are getting.

In case your are new to Lambda functions: anything you console.log will show up in AWS Cloudwatch assuming the Lambda function is executing under a role that allows it to write to Cloudwatch. I think there is a built-in role like lambda-basic-execution or something. You can set the role in the Lambda function under "Execution role" in the AWS Lambda console. Once that is working, go to the AWS Cloudwatch console, click "Logs", select the /aws/lambda/{yourfunctionname} log, click "Search Log Group" and then I usually filter on the last five minutes of logs by clicking on "5m" in the upper right.

If you are still stuck let me know.

-Mark