Alexa function works in AWS lamda, but not from the Service Simulator - amazon-web-services

I am relatively new to AWS and Alexa skills. I am building a simple custom skill that gives you a dressing advice depending on the weather.
I have 2 custom intents : dressingTodayIntent & dressingTomorrowIntent. In the Service Simulator of the developer portal, my two intents don't work, I do get a lambda response though, but with an undefined outputSpeech, like this:
{
"version": "1.0",
"response": {
"outputSpeech": {
"type": "SSML",
"ssml": "<speak> undefined </speak>"
},
"card": null,
"reprompt": null,
"speechletResponse": {
"outputSpeech": {
"id": null,
"ssml": "<speak> undefined </speak>"
},
"card": null,
"directives": null,
"reprompt": null,
"shouldEndSession": true
}
},
"sessionAttributes": {}
}
Could it be a scope issue in my intent code?
'DressingTodayIntent': function() {
var dressingAdvice;
var speechOutput = getJSON('https://api.darksky.net/forecast/9e0495a835ed823a705a9a567eee982a/48.861317,2.348764?units=si&exclude=currently,minutely,hourly,alerts,flags',
function(err, forecast) {
if (err) {
console.log('Error occurred while trying to retrieve weather data', err);
} else {
dressingAdvice = getDressingAdvice(forecast, true);
console.log("one " + dressingAdvice);
}
console.log("two " + dressingAdvice);
return dressingAdvice;
});
console.log("three " + speechOutput);
this.response.cardRenderer("Your dressing advice for today:", speechOutput);
this.response.speak(speechOutput);
this.emit(':responseReady');
},
In AWS Lambda, I see a correct output for the first 2 logs, and an error for the 3rd one:
first log: "one " + dressingAdvice, as expected
second log: "two " + dressingAdvice, as expected
third log: "three " + undefined
Thank you for you help!

When you say "tested from AWS Lambda", I assume that you mean using the AWS console to send a JSON test message to the Lambda, then looking at the response JSON to determine if it is correct?
If so, make sure that it matches the JSON sent to/from the Alexa test page in the dev portal. Sounds like they might be different.
Also, make sure that you are linked to the correct ARN in the Alexa skill.

The undefined is likely a variable scope issue in the code.
I noticed in your response that you don't have any sessionAttributes. Is your code setting or pulling the value for the response from a session value? If so, the values need to be sent back with the sessionAttributes.

I figured out what was wrong, I needed to move the response code into the callback function, like this:
'DressingTodayIntent': function() {
var speechOutput;
var self = this;
var dressingAdvice = getJSON('https://api.darksky.net/forecast/9e0495a835ed823a705a9a567eee982a/48.861317,2.348764?units=si!ude=currently,minutely,hourly,alerts,flags',
function(err, forecast) {
if (err) {
console.log('Error occurred while trying to retrieve weather data', err);
} else {
speechOutput = getDressingAdvice(forecast, true);
}
self.response.cardRenderer("Your dressing advice for today:", speechOutput);
self.response.speak(speechOutput);
self.emit(':responseReady');
});
},

Related

lambda function returning null for deleting item in DynamoDB

hi Ive been trying to get my lambda function to delete an item in dynamo db but the function is simply returning null and i have no idea how to even start debugging it, hoping someone here has the knowledge to help
my table has guid as its primary partition key and username as its sort key
heres my code in .js
const AWS = require("aws-sdk");
// Initialising the DynamoDB SDK
const documentClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event) => {
const { guid, username } = event
const params = {
TableName: "Items", // The name of your DynamoDB table
Key:{
"guid": {"S" : guid},
"username": {"S" : username}
}
};
try {
// Utilising the scan method to get all items in the table
documentClient.delete(params, function(err, data) {
if (err) {
return("Unable to delete item. Error JSON:", JSON.stringify(err, null, 2));
} else {
return("DeleteItem succeeded:", JSON.stringify(data, null, 2));
}
});
}
catch (e) {
return {
statusCode: 500,
body: e
};
}
};
this is the payload for the test event im using in lambda
{
"guid": "34",
"username": "newusername"
}
You are using async function handler. So your function probably just finishes before your code actually has a chance to execute.
You can overcome this issue by wrapping your code around new Promise as shown in the docs

How to properly set an API call in QML using XMLHttpRequest

I am building a small weather API as exercise to use QML and properly operate an API call using OpenWeather and you can see there a typical API response.
The problem I am having is that I can't get the API call to work. After setting a minimal example with some cities that you can see below, right next to the city it should appear the symbol of the weather, but it does not happen. The list of the icon can be found here. Source code of the MVE can be found here for completeness.
The error from the compiler: qrc:/main.qml:282: SyntaxError: JSON.parse: Parse error
This is what is happening
This is what is expected
Typical API JSON response can be found both here and below:
{
"coord": {
"lon": -122.08,
"lat": 37.39
},
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}
],
"base": "stations",
"main": {
"temp": 282.55,
"feels_like": 281.86,
"temp_min": 280.37,
"temp_max": 284.26,
"pressure": 1023,
"humidity": 100
},
"visibility": 16093,
"wind": {
"speed": 1.5,
"deg": 350
},
"clouds": {
"all": 1
},
"dt": 1560350645,
"sys": {
"type": 1,
"id": 5122,
"message": 0.0139,
"country": "US",
"sunrise": 1560343627,
"sunset": 1560396563
},
"timezone": -25200,
"id": 420006353,
"name": "Mountain View",
"cod": 200
}
Below a snippet of code related to the API call:
main.qml
// Create the API getcondition to get JSON data of weather
function getCondition(location, index) {
var res
var url = "api.openweathermap.org/data/2.5/weather?id={city id}&appid={your api key}"
var doc = new XMLHttpRequest()
// parse JSON data and put code result into codeList
doc.onreadystatechange = function() {
if(doc.readyState === XMLHttpRequest.DONE) {
res = doc.responseText
// parse data
var obj = JSON.parse(res) // <-- Error Here
if(typeof(obj) == 'object') {
if(obj.hasOwnProperty('query')) {
var ch = onj.query.results.channel
var item = ch.item
codeList[index] = item.condition["code"]
}
}
}
}
doc.open('GET', url, true)
doc.send()
}
In order to solve this problem I consulted several sources, first of all : official documentation and the related function. I believe it is correctly set, but I added the reference for completeness.
Also I came across this one which explained how to simply apply XMLHttpRequest.
Also I dug more into the problem to find a solution and also consulted this one which also explained how to apply the JSON parsing function. But still something is not correct.
Thanks for pointing in the right direction for solving this problem.
Below the answer to my question. I was not reading properly the JSON file and after console logging the problem the solution is below. code was correct from beginning, only the response needed to be reviewed properly and in great detail being the JSON response a bit confusing:
function getCondition() {
var request = new XMLHttpRequest()
request.open('GET', 'http://api.openweathermap.org/data/2.5/weather?q=London&units=metric&appid=key', true);
request.onreadystatechange = function() {
if (request.readyState === XMLHttpRequest.DONE) {
if (request.status && request.status === 200) {
console.log("response", request.responseText)
var result = JSON.parse(request.responseText)
} else {
console.log("HTTP:", request.status, request.statusText)
}
}
}
request.send()
}
Hope that helps!
In your code, your url shows this: "api.openweathermap.org/data/2.5/weather?id={city id}&appid={your api key}". You need to replace {city id} and {your api key} with real values.
You can solve it by providing an actual city ID and API key in your request URL

AWS lambda - how to use conditionals depending on if query parameters exist?

I want my function to return a list of everything in a table if there are no query parameters, and a single row if the parameter id exists
var mysql = require('mysql');
var config = require('./config.json');
var pool = mysql.createPool({
host : config.host,
user : config.user,
password : config.password,
database : config.database
});
exports.handler = (event, context, callback) => {
var whereClause
if(event.queryStringParameters.id !== null){
let id = event.queryStringParameters.id
whereClause = ' where id='+id
}
context.callbackWaitsForEmptyEventLoop = false;
pool.getConnection(function(err, connection) {
// Use the connection
connection.query('SELECT * from users'+whereClause, function (error, results, fields) {
// And done with the connection.
connection.release();
// Handle error after the release.
if (err) callback(err);
else {
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(results),
"isBase64Encoded": false
};
callback(null, response);
}
});
});
};
the function fails when no query parameter is present with the error
"Cannot read property 'id' of null"
why is that?
You didn't supply any line number information or a stack trace, so I'm guessing this if statement fails because event.queryStringParameters is null:
if(event.queryStringParameters.id !== null)
let id = event.queryStringParameters.id
whereClause = ' where id='+id
}
And you should instead write:
if (event.queryStringParameters && event.queryStringParameters.id !== null) {
let id = event.queryStringParameters.id;
whereClause = ' where id=' + id;
}
Having said that, you should not inject user-supplied values (such as id) into SQL queries using string concatenation. This opens you up to a SQL Injection attack. Here are ideas for how to write this code more safely: How to prevent SQL Injection in Node.js
Do you use AWS Lambda with Amazon API Gateway?
AWS Lambda with Amazon API Gateway
In this case:
Make sure that you create a body mapping template in API gateway (Integration Request->Body Mapping Templates). As an example here's a body mapping template that would pass along the query parameter email to your lambda function: { "id": "$input.params('id')" }
AWS Developer Forum

Cognito UnknownError after turn on device registration

As per requirement, I need to turn on device registration to Always. However, our SRP flow starts failing with the below issue.
{ code: 'UnknownError', message: 'Unknown error, the response body from fetch is: undefined' }
After doing some research, I found one similar post, but it seems like the only solution is to turn device registration off.
It's failing while running node get-token.js script to retrieve token for our CI/CD testing pipeline.
cognitoUser.authenticateUser(authCfg, {
onSuccess: function (result) {
console.log("Result : ", result);
const token = result.getAccessToken().getJwtToken();
resolve(token)
},
onFailure: function(err) {
console.error("Failure : ", err);
console.log(new Error().stack);
reject(new Error("An error occurred: " + err))
},
newPasswordRequired: function (userAttributes, requiredAttributes) {
cognitoUser.completeNewPasswordChallenge(p, userAttributes, this);
},
});
Seems like I missed the point mentioned in this post . Adding the below code works.
const WindowMock = require('window-mock');
global.window = {localStorage: WindowMock.localStorage};
global.navigator = () => null;

How to get result of AWS lambda function running with step function

I am using AWS step function to invoke lambda function like this.
return stepfunctions.startExecution(params).promise().then((result) => {
console.log(result);
console.log(result.output);
return result;
})
And result is
{ executionArn: 'arn:aws:states:eu-west-2:695510026694:...........:7c197be6-9dca-4bef-966a-ae9ad327bf23',
startDate: 2018-07-09T07:35:14.930Z }
But i want the result as output of final lambda function
I am going through https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/StepFunctions.html#sendTaskSuccess-property
There are multible function there i am confused which one could be used to get back result of final lambda function.
Same question is there on stackoverflow Api gateway get output results from step function? i dont want to call any function periodically and keep checking status.Even if i use DescribeExecution function periodically i will only get the status of execution but not the result i wanted. Is there any way or any function which returns promise and is resolved once all the lambda has executed and give back the result
You can't get back a result from a step function execution in a synchronous way.
Instead of polling the result of the step function on completion send a result to an SNS topic or SQS queue for further processing in the final lambda function or model the whole process in the step function state machine.
After doing some study and looking at various tutorial i realized that this stackoverflow answer Api gateway get output results from step function? gives a easier approach to solve the problem and get final result from step function, yes i am not sure about another approach and how to implement any new answer is always appreciated
This is my code to implement the same approach this might help someone.
// in function first start step function execution using startExecution()
var params = {
stateMachineArn: 'some correct ARN',
input: JSON.stringify(body)
};
return stepfunctions.startExecution(params).promise().then((result) => {
var paramsStatus = {
executionArn: result.executionArn
};
var finalResponse = new Promise(function(resolve,reject){
var checkStatusOfStepFunction = setInterval(function(){
//on regular interval keep checking status of step function
stepfunctions.describeExecution(paramsStatus, function(err, data) {
console.log('called describeExecution:', data.status);
if (err){
clearInterval(checkStatusOfStepFunction);
reject(err);
}
else {
if(data.status !== 'RUNNING'){
// once we get status is not running means step function execution is now finished and we get result as data.output
clearInterval(checkStatusOfStepFunction);
resolve(data.output);
}
}
});
},200);
});
return finalResponse
})
To be able to get the result of step function (example: combined gateway & step function). You need to:
1. startExecution,
2. wait for your state machine to finish the execution (to be sure make wait equivalent to timeout of your state machine => wait = TimeoutSeconds of your state machine)
3. call describeExecution with the receive executionArn from startExecution.
Note that startExecution is an async function and it's not waiting for the result.
In my case, I'm using Lambda named init to execute the 3 discussed steps:
Code lambda Init:
const AWS = require('aws-sdk')
exports.handler = async (event) => {
const stepFunctions = new AWS.StepFunctions();
const reqBody = event.body || {};
const params = {
stateMachineArn: process.en.stateMachineArn,
input: JSON.stringify(reqBody)
}
return stepFunctions.startExecution(params).promise()
.then(async data => {
console.log('==> data: ', data)
await new Promise(r => setTimeout(r, 6000));
return stepFunctions.describeExecution({ executionArn: data.executionArn }).promise();
})
.then(result => {
return {
statusCode: 200,
message: JSON.stringify(result)
}
})
.catch(err => {
console.error('err: ', err)
return {
statusCode: 500,
message: JSON.stringify({ message: 'facing error' })
}
})
}
Code stateMachine
Make sure that in your statemachine your returning "ResultPath".
{
"Comment": "Annoucement validation",
"StartAt": "contact-validation",
"Version": "1.0",
"TimeoutSeconds": 5,
"States": {
"contact-validation": {
"Type": "Task",
"Resource": "arn:aws:xxxxxxx:function:scam-detection-dev-contact",
"ResultPath": "$.res",
"Next": "WaitSeconds"
},
"WaitSeconds": {
"Type": "Wait",
"Seconds": 1,
"Next": "Result"
},
"Result": {
"Type": "Pass",
"ResultPath": "$.res",
"End": true
}
}
}