Unexpected Parsing Error On AWS Lambda JS - javascript

I think this is a syntax error but I'm having trouble finding documentation. I keep getting 'Parsing Error: Unexpected Token {". It says its to do with the 'YesIntent', but won't give specifics. I'm new to JS, but I can't see what could be the problem. Every '{' has a matching '}'.
Any insights would be appreciated. Thank you.
const Alexa = require("alexa-sdk");
const appId = ''; //'';
exports.handler = function(event, context, callback) {
const alexa = Alexa.handler(event, context);
alexa.appId = appId;
alexa.registerHandlers(handlers);
alexa.execute();
};
const handlers = {
'LaunchRequest': function() {
this.emit('YesIntent');
},
'YesIntent': function() {
getData(callback(title) {
this.response.speak('Here are your data ' + title);
this.emit(':responseReady');
}),
};
function getData() {
var ddb = new AWS.DynamoDB.DocumentClient({
region: 'us-west-1'
});
var params = {
TableName: 'WallyFlow_StartTime',
Key: 'TimeStamp',
};
ddb.get(params, function(err, data) {
if (err) {
callback(err, null);
} else {
title = data.Item.title;
}
});
}

Sorry, in this style you need more braces :) Updated to:
'YesIntent': function () {
getData( {
callback(title) {
this.response.speak('Here are your data ' + title);
this.emit(':responseReady');
}})
}};

I suspect it should be something like this. callback should be the name of the parameter to the getData() function, not something you call in the argument. The argument to getData() should be a function.
And getData() should call the callback function in the non-error case as well as the error case.
You also need an extra } to end the handlers object, and the end of the statement that calls getData() should be ;, not ,.
const handlers = {
'LaunchRequest': function() {
this.emit('YesIntent');
},
'YesIntent': function() {
getData(function(title) {
this.response.speak('Here are your data ' + title);
this.emit(':responseReady');
});
}
};
function getData(callback) {
var ddb = new AWS.DynamoDB.DocumentClient({
region: 'us-west-1'
});
var params = {
TableName: 'WallyFlow_StartTime',
Key: 'TimeStamp',
};
ddb.get(params, function(err, data) {
if (err) {
callback(err, null);
} else {
title = data.Item.title;
callback(title);
}
});
}

Related

Switch case can't evaluate object property in nested callbacks

This function receives an object parameter like {'status': 'info'}, but I can't access it inside some callbacks. Shouldn't it be like a global variable for all the code block?:
var msg = {
status: 'good',
data: 'cats'
};
function status(msg) {
console.log(msg.status);
doSomething(msg.data, function(err, reply) {
doSomething2(reply, function(err, data) {
switch(msg.status) { // error: cannot get status of undefined
case 'info':
console.log('Cat info.');
}
});
});
}
function doSomething(data, cb) {
return 'burdz';
}
function doSomething2(data, cb) {
return data + ' and dogs';
}
status();
Throws an error on accessing the object property of message in the switch case. I can access the property before the doSomething functions, but not inside the last one.
const msg = {
status: 'good',
data: 'cats'
};
function status(msg) {
console.log(msg.status);
doSomething(msg.data, function(err, reply) {
doSomething2(reply, function(err, data) {
switch(msg.status) {
case 'info':
console.log('Cat info.');
}
});
});
}
function doSomething(data, cb) {
return 'burdz';
}
function doSomething2(data, cb) {
return data + ' and dogs';
}
status(msg);
You need to pass something to status! Use also const instead of let if your msg is declared in the global state
Interesting lecture about scopes and closures. https://github.com/getify/You-Dont-Know-JS/tree/master/scope%20%26%20closures

Test and stub params in POST request

Guys how can I stub params in POST request, for example here a part of function
gridCalculator : function(req,res){
// calculation logic
var options=[];
options.dateFirstLicensed = req.param('DateFirstLicensed');
options.dateFirstInsured = req.param('DateFirstInsured');
options.claimList = req.param('ClaimList');
options.suspenList = req.param('SuspenList');
...etc
if I did this
it('grid Calculate', function (done) {
var req = {
'DateFirstLicensed' : "01-01-2010",
'DateFirstInsured': "01-01-2011",
'ClaimList': ['05-03-2012'],
'SuspenList': [{'StartDate':'05-03-2012','EndDate':'05-05-2012' }]
};
gridCalculator.gridCalculator(req,function (err, result) {
result.should.exist;
done();
});
});
I get error because I'm simply passing an object not POST request
TypeError: req.param is not a function
Two options come to mind (there are probably a lot more):
Option 1: Define the param function yourself:
it('grid Calculate', function (done) {
var params = function(param) {
switch (param) {
case 'DateFirstLicensed':
return "01-01-2010";
case 'DateFirstInsured':
... //do the same for all params
}
};
var req = {
param: params
};
gridCalculator.gridCalculator(req,function (err, result) {
result.should.exist;
done();
});
});
Option 2: Use tools like supertest to create calls to your server's endpoint.
The problem is that you don't stub the function that is used in your gridCalculator method in your test.
It should look like this:
it('grid Calculate', function (done) {
var testParams = {
'DateFirstLicensed' : "01-01-2010",
'DateFirstInsured': "01-01-2011",
'ClaimList': ['05-03-2012'],
'SuspenList': [{'StartDate':'05-03-2012','EndDate':'05-05-2012'}]
};
var req = {
param: function (paramName) {
return testParams[paramName];
}
};
gridCalculator.gridCalculator(req,function (err, result) {
result.should.exist;
done();
});
});

What is wrong with my async.series

I am trying to execute an async series in nodejs with https://github.com/caolan/async#series :
async.series([
function getLightsId(callback) {
args = {
path: {
"username": "username"
}
};
client.registerMethod("getLightState", "http://bridgeip/api/${username}/lights/", "GET");
client.methods.getLightState(args, function (data, response) {
var ids = [];
for (key in data) {
ids.push(key);
}
callback(null, ids);
});
},
function getLightsState(ids, callback) {
var lightsState = new Object();
async.each(ids, function (id) {
getLightState(id, function (state) {
lightsState[id] = state;
});
});
callback(null, lightsState);
}
], function (err, result) {
console.log(result);
});
but its returning me this error :
callback(null, lightsState); TypeError : undefined is not a function.
I dont understand why..
How do i pass my object lightsState to my other function ?
You won't get ids in your second task, but you'll get a callback as is described in docs.
You could do:
async.series([
function getLightsId(callback) {
args = {
path: {
"username": "username"
}
};
client.registerMethod("getLightState", "http://bridgeip/api/${username}/lights/", "GET");
client.methods.getLightState(args, function (data, response) {
var ids = [];
for (key in data) {
ids.push(key);
}
callback(null, ids);
});
},
], function (err, result) {
var ids = result[0]
var lightsState = new Object();
async.each(ids, function (id) {
getLightState(id, function (state) {
lightsState[id] = state;
});
});
});
But this goes against the conventional use-case of async.series, you only have one async task there.
What you're after for is probably async.waterfall - so here's how that would look like:
async.waterfall([
function getLightsId(callback) {
args = {
path: {
"username": "username"
}
};
client.registerMethod("getLightState", "http://bridgeip/api/${username}/lights/", "GET");
client.methods.getLightState(args, function (data, response) {
var ids = [];
for (key in data) {
ids.push(key);
}
callback(null, ids);
});
},
function getLightsState(ids, callback) {
var lightsState = new Object();
async.each(ids, function (id) {
getLightState(id, function (state) {
lightsState[id] = state;
});
}, function(err) {
callback(err, lightsState)
});
}
], function (err, result) {
console.log(result);
});
Oh, and I thought that async.each needed a callback. I'm not sure, maybe it was you intention not to be so?
its returning me this error: callback(null, lightsState); TypeError : undefined is not a function.
Because callback is not a function. Only the first argument that is passed to your function getLightsState(ids, callback) { is one. Check the docs for series:
tasks - An array or object containing functions to run, each function is passed a
callback(err, result)
series(tasks) runs the functions in the tasks array in series, each one running once the previous function has completed.
In contrast, these are the docs for the waterfall function - which is what you want:
Runs the tasks array of functions in series, each passing their results to the next in the array.
Btw,
var lightsState = new Object();
async.each(ids, function (id) {
getLightState(id, function (state) {
lightsState[id] = state;
});
});
callback(null, lightsState);
is unlikely to work, you probably want to use reduce instead:
async.reduce(ids, {}, function(lightsState, id, rcallback) {
getLightState(id, function(state) {
lightsState[id] = state;
rcallback(null, lightsState);
});
}, callback);

How can I use node async to fetch my mongoose calls?

I'm building a site with node/express/mongoose and it needs to do the following things when viewing a submission.
The problem I'm running into is doing db fetches in a non-serial fashion. For example, I'll do a few calls to fetch some data, but some of the calls might not finish until the execution context goes to the other. Tried to use the npm module, async, but am having trouble trying to figure out how I would integrate it.
Here is my code:
var getViewCount = function(submissionId) {
Submission.getSubmissionViewCount({
submissionId : submissionId
}, function(err, count) {
if (err) {
throw err;
}
if (count) {
return count;
}
});
};
var getVotes = function(submissionId) {
console.log('getvotes');
Submission.getSubmissionVotes({
submissionId : submissionId
}, function(err, votes) {
return votes;
});
};
var getSubmission = function(id) {
Submission.getSubmission({
id : id
}, function(err, submission) {
if (err) {
throw err;
}
if (submission) {
return submission;
}
});
};
var renderSubmission = function(title, submission, views) {
res.render('submission', {
title: submission.title + ' -',
submission: submission,
views: views.length
});
};
How do I use this with async? Or should I be using async.series isntead of async.async?
async.series([
function(callback) {
var submission = getSubmission(id);
callback(null, submission);
},
function(callback) {
// getViewCount(submissionId);
},
function(callback) {
// getVotes(submissionId);
},
function(callback) {
//renderSubmission(title, submission, views);
}
], function(err, results) {
console.log(results);
});
Basically I want to fetch the views and votes first and then render my submission.
TheBrain's description of the overall structural changes that you should make to your code is accurate. The basic methodology in Node is to nest a series of callbacks; very rarely should you require functions that actually return a value. Instead, you define a function that takes a callback as parameter and pass the result into that callback. Please review the code below for clarification (where cb is a callback function):
var getViewCount = function(submissionId, cb) {
Submission.getSubmissionViewCount({
submissionId : submissionId
}, function(err, count) {
if (err) {
throw err;
}
if (cb) {
cb(count);
}
});
};
var getVotes = function(submissionId, cb) {
console.log('getvotes');
Submission.getSubmissionVotes({
submissionId : submissionId
}, function(err, votes) {
if (cb) {
cb(votes);
}
});
};
var getSubmission = function(id, cb) {
Submission.getSubmission({
id : id
}, function(err, submission) {
if (err) {
throw err;
}
if (cb) {
cb(submission);
}
});
};
var renderSubmission = function(submissionId) {
getSubmission(submissionId, function (submission) {
if (!submission) {
// unable to find submission
// add proper error handling here
} else {
getViewCount(submissionId, function (viewCount) {
res.render('submission', {
title: submission.title + ' -',
submission: submission,
views: viewCount
});
});
}
};
};

Node.js: What techniques are there for writing clean, simple callback code?

node.js code is known for turning into callback spaghetti.
What are the best techniques for overcoming this problem and writing clean, uncomplex, easy to understand callback code in node.js?
Take a look at Promises: http://promises-aplus.github.io/promises-spec/
It is an open standard which intended to solve this issue.
I am using node module 'q', which implements this standard: https://github.com/kriskowal/q
Simple use case:
var Q = require('q');
For example we have method like:
var foo = function(id) {
var qdef = Q.defer();
Model.find(id).success(function(result) {
qdef.resolve(result);
});
return (qdef.promise);
}
Then we can chain promises by method .then():
foo(<any-id>)
.then(function(result) {
// another promise
})
.then(function() {
// so on
});
It is also possible to creating promise from values like:
Q([]).then(function(val) { val.push('foo') });
And much more, see docs.
See also:
http://jeditoolkit.com/2012/04/26/code-logic-not-mechanics.html#post
http://wiki.commonjs.org/wiki/Promises/A
Several things can be done to avoid the 'matrioska-style'.
You can store callbacks to variables:
var on_read = function (foo, bar) {
// some logic
},
on_insert = function (err, data) {
someAsyncRead(data, on_read);
};
someAsyncInsert('foo', on_insert);
You can use some modules that help in those scenarios.
// Example using funk
var funk = require('funk');
for(var i = 0; i < 10; i++) {
asyncFunction(i, funk.add(function (data) {
this[i] = data;
}));
}
funk.parallel(function () {
console.log(this);
});
I'd suggest 1) using CoffeeScript and 2) using named callbacks and passing state between them in a hash, rather than either nesting callbacks or allowing argument lists to get very long. So instead of
var callback1 = function(foo) {
var callback2 = function(bar) {
var callback3 = function(baz) {
doLastThing(foo, bar, baz);
}
doSomethingElse(bar, callback3);
}
doSomething(foo, callback2);
}
someAsync(callback1);
you can instead simply write
callback1 = (state) -> doSomething state.foo, callback2
callback2 = (state) -> doSomethingElse state.bar, callback3
callback3 = (state) -> doLastThing state
someAsync callback1
once your doSomething, doSomethingElse and doLastThing have been rewritten to use/extend a hash. (You may need to write extra wrappers around external functions.)
As you can see, the code in this approach reads neatly and linearly. And because all callbacks are exposed, unit testing becomes much easier.
Try node-line
https://github.com/kevin0571/node-line
Usage:
var line = require("line");
line(function(next) {
obj.action1(param1, function(err, rs) {
next({
err: err,
rs: rs
});
});
}, function(next, data) {
if (data.err) {
console.error(err);
return;
}
obj.action2(param2, function(err, rs) {
if (err) {
console.error(err);
return;
}
next(rs);
});
}, function(rs) {
obj.finish(rs);
});
For the most part, working Twitter OAuth2 application-only example, using Kris' Q promise library with https.request, Nodejs Express api route. First attempt user timeline GET. If 401 response, refreshing bearer-token then retry user timeline. I had to use Q.when to handle a promise that returns another promise (chaining) or a value.
/**
* Using Rails-like standard naming convention for endpoints.
* GET /things -> index
* POST /things -> create
* GET /things/:id -> show
* PUT /things/:id -> update
* DELETE /things/:id -> destroy
*/
'use strict';
// var _ = require('lodash');
var http = require('http');
var https = require('https');
var querystring = require('querystring');
var Q = require('q')
// Get list of twtimelines
exports.index = function(req, res) {
var tid = req.query.tid
if (tid) {
Q.when(reqTimeline(tid, true, res), function(value) {
// > value
// 404
// > body1
// '{"errors":[{"code":34,"message":"Sorry, that page does not exist."}]}'
})
} else {
res.json({
errors: [{
message: 'no tid specified in query'
}]
});
}
};
function reqPromise(options, postData) {
var deferred = Q.defer()
var req = https.request(options, function(res) {
// console.log("statusCode: ", res.statusCode);
// console.log("headers: ", res.headers);
var statusCode = res.statusCode
deferred.notify(res)
res.on('data', function(d) {
//process.stdout.write(d);
deferred.notify(d)
}).on('end', function() {
deferred.resolve(statusCode)
});
});
req.on('error', function(e) {
console.error(e);
deferred.reject(e)
});
req.write(postData);
req.end();
return deferred.promise
} // deferRequest
function isIncomingMessage(ot) {
return ot instanceof http.IncomingMessage
}
function isBuffer(ot) {
return ot instanceof Buffer
}
function reqTimeline(screen_name, reqBearerTokenOn401, res) {
var optionsUserTimeline = {
hostname: 'api.twitter.com',
path: '/1.1/statuses/user_timeline.json?' + querystring.stringify({
count: '3',
screen_name: screen_name
}),
method: 'GET',
headers: {
//'Authorization': 'Bearer ' + JSON.parse(body1).access_token
'Authorization': 'Bearer ' + process.env.BEARER_TOKEN
} // headers
};
console.log("optionsUserTimeline", optionsUserTimeline)
var statusCode;
var body1 = new Buffer(''); // default utf8 string buffer ?
return reqPromise(optionsUserTimeline, '')
.then(function(value) { // done
if (reqBearerTokenOn401 && value === 401) {
console.log("reqTimeline - requesting bearer token")
return reqBearerToken(screen_name, res)
}
console.log("reqTimeline - done done:", value)
res.end()
return value
},
function(reason) { // error
console.log("reqTimeline - error:", body1)
},
function(progress) {
console.log("reqTimeline - progress:", body1)
if (isIncomingMessage(progress)) {
body1 = body1.slice(0, 0) // re-set buffer
statusCode = progress.statusCode;
if (reqBearerTokenOn401 && statusCode === 401) {
// readyn for retry
} else {
res.writeHead(statusCode)
}
} else if (isBuffer(progress)) {
if (reqBearerTokenOn401 && statusCode === 401) {
body1 += progress
} else {
res.write(progress)
}
} else {
throw "reqTimeline - unexpected progress"
}
});
} // reqTimeline
function reqBearerToken(screen_name, res) {
var postData = querystring.stringify({
'grant_type': 'client_credentials'
})
var optionsBearerToken = {
hostname: 'api.twitter.com',
path: '/oauth2/token',
method: 'POST',
headers: {
'Authorization': 'Basic ' + new Buffer(
process.env.CONSUMER_KEY + ":" + process.env.CONSUMER_SECRET
).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
'Content-Length': postData.length
} // headers
}
// console.log("key", process.env.CONSUMER_KEY)
// console.log("secret", process.env.CONSUMER_SECRET)
// console.log("buf", new Buffer(
// process.env.CONSUMER_KEY + ":" + process.env.CONSUMER_SECRET
// ).toString())
console.log("optionsBearerToken", optionsBearerToken)
var body2 = new Buffer(''); // default utf8 string buffer ?
return reqPromise(optionsBearerToken, postData)
.then(function(value) { // done
console.log("reqBearerToken - done:", body2)
if (value === 200) {
console.log("reqBearerToken - done done")
process.env.BEARER_TOKEN = JSON.parse(body2).access_token;
return reqTimeline(screen_name, false, res)
}
return value
}, function(reason) {
throw "reqBearerToken - " + reason
}, function(progress) {
if (isIncomingMessage(progress)) {
body2 = body2.slice(0, 0) // reset buffer
} else if (isBuffer) {
body2 += progress
} else {
throw "reqBearerToken - unexpected progress"
}
});
} // reqBearerToken

Categories