Code works locally but not on AWS lambda - javascript

the following lamdba code works perfectly fine when testing locally using Alex-app-server but when published and tested on AWS Lambda, it gets within the else statement and prints the console.log('OUT PUBLISH') But it doesn't publish the 'lambda/channelnumber' nor does it send the correct response back to me or print 'IN PUBLISH'
Any ideas why its just completing the bottom half of the else statement and not touching the publish function?
Code Snippet where I believe the problem lies
function (request, response) {
var channelNumber = request.slot('CHANNELNUMBER');
if (_.isEmpty(channelNumber)) {
var prompt = 'I didn\'t hear a channel code. Tell me a channel code.';
response.say(prompt).shouldEndSession(true);
return true;
} else {
//Doesn't publish any of this?????
thingShadows.publish('lambda/channelNumber', channelNumber, function () {
var prompt1 = 'Okay.';
response.say(prompt1).shouldEndSession(true);
console.log('in publish');
});
////But prints this??
console.log('out publish');
return true;
}
}
Full Code
'use strict';
module.change_code = 1;
var Alexa = require('alexa-app');
var skill = new Alexa.app('smartmote');
var awsIot = require('aws-iot-device-sdk');
var deviceName = "tv";
var _ = require('lodash');
var path = require('path');
var host = "XXXXXXXXXXXXXXXXXXXX.iot.us-east-1.amazonaws.com";
//App id is the skill being used.
var app_id = "amzn1.ask.skill.YYYYYYYYYYYYYYYYYYYYY";
var thingShadows = awsIot.thingShadow({
keyPath: path.join(__dirname, '/Raspi.private.key'),
certPath: path.join(__dirname, '/Raspi.cert.pem'),
caPath: path.join(__dirname, '/root-CA.crt'),
clientId: deviceName,
region: "us-east-1",
});
var reprompt = 'I didn\'t hear a channel, tell me a channel number or name to change to that channel';
skill.launch(function (request, response) {
var prompt = 'To change channel, tell me a channel number.';
response.say(prompt).reprompt(reprompt).shouldEndSession(true);
});
skill.intent('ChannelNumberIntent', {
'slots': {
'CHANNELNUMBER': 'CHANNELID'
},
'utterances': ['{|Change|put} {|the|on} {|channel} {|to} {-|CHANNELNUMBER}']
},
function (request, response) {
var channelNumber = request.slot('CHANNELNUMBER');
if (_.isEmpty(channelNumber)) {
var prompt = 'I didn\'t hear a channel code. Tell me a channel code.';
response.say(prompt).shouldEndSession(true);
return true;
} else {
thingShadows.publish('lambda/channelNumber', channelNumber, function () {
console.log('in pub');
var prompt1 = 'Okay.';
response.say(prompt1).shouldEndSession(true);
callback();
});
console.log('out pub');
return true;
}
}
);
module.exports = skill;

This is most likely because of the asynchronous nature of your code.
You haven't told us what thingShadows.publish() does, but it appears to take a callback function as its second argument. Presumably this function will be called when publish() has finished doing whatever it does.
When running locally I would imagine that the output you see is (in this order):
out publish
in publish
Notice that out publish gets called before in publish. This is because the publish method is asynchronous, so execution will continue as soon as it is called. In your case, you are calling return immediately after calling publish, which probably means your lambda job is ending before it has a chance to log in publish.
You haven't provided enough information about the rest of your lambda code/setup to provide a full answer, but you need to make sure that you are waiting for your publish method to have finished before continuing. One way to achieve this is to use the callback object that is passed to your lambda handler:
exports.myHandler = function(event, context, callback) {
// Other code
thingShadows.publish('lambda/channelNumber', channelNumber, function () {
var prompt1 = 'Okay.';
response.say(prompt1).shouldEndSession(true);
console.log('in publish');
// When the publish method is complete, we can call `callback`
// to tell lambda we are done
callback();
});
}

Related

NodeJS - where to place functions for eval()

I have a NodeJS-Server which communicated with the fronend via Websocket-Connection.
When the Server gets a on('message'), it should run a function which name is given the message via eval().
it workes fine, unless I completely don't know where to put the funcions to be called.
var http = require('http');
var ws = require('ws');
function render(vars) {
var server = http.createServer(function(req, res) {
.....
});
/* WEBSOCKET */
var wsServer = new ws.Server({server});
wsServer.on('connection', socket => {
socket.on('message', message => {
console.log('WS from template <-- ', message);
var wsIn = JSON.parse(message);
eval(wsIn.action);
});
});
}
when a message is incoming, eval(wsIn.action) should run a function called.. .lets assume runme.. so where would I now need to declare this function ? I try everything but whatever I do, i get
ReferenceError:: runme is not defined
edit:
I found out something interesting:
when i call a function normal like runme(); in my onMessage.. everything is cool.. but with eval(runme); nothing happens.. no error, no output, nothing..

Node HTTP request hangs forever

We've got a Node.js script that is run once a minute to check the status of our apps. Usually, it works just fine. If the service is up, it exits with 0. If it's down, it exits with 1. All is well.
But every once in a while, it just kinda stops. The console reports "Calling status API..." and stops there indefinitely. It doesn't even timeout at Node's built-in two-minute timeout. No errors, nothing. It just sits there, waiting, forever. This is a problem, because it blocks following status check jobs from running.
At this point, my whole team has looked at it and none of us can figure out what circumstance could make it hang. We've built in a start-to-finish timeout, so that we can move on to the next job, but that essentially skips a status check and creates blind spots. So, I open the question to you fine folks.
Here's the script (with names/urls removed):
#!/usr/bin/env node
// SETTINGS: -------------------------------------------------------------------------------------------------
/** URL to contact for status information. */
const STATUS_API = process.env.STATUS_API;
/** Number of attempts to make before reporting as a failure. */
const ATTEMPT_LIMIT = 3;
/** Amount of time to wait before starting another attempt, in milliseconds. */
const ATTEMPT_DELAY = 5000;
// RUNTIME: --------------------------------------------------------------------------------------------------
const URL = require('url');
const https = require('https');
// Make the first attempt.
make_attempt(1, STATUS_API);
// FUNCTIONS: ------------------------------------------------------------------------------------------------
function make_attempt(attempt_number, url) {
console.log('\n\nCONNECTION ATTEMPT:', attempt_number);
check_status(url, function (success) {
console.log('\nAttempt', success ? 'PASSED' : 'FAILED');
// If this attempt succeeded, report success.
if (success) {
console.log('\nSTATUS CHECK PASSED after', attempt_number, 'attempt(s).');
process.exit(0);
}
// Otherwise, if we have additional attempts, try again.
else if (attempt_number < ATTEMPT_LIMIT) {
setTimeout(make_attempt.bind(null, attempt_number + 1, url), ATTEMPT_DELAY);
}
// Otherwise, we're out of attempts. Report failure.
else {
console.log("\nSTATUS CHECK FAILED");
process.exit(1);
}
})
}
function check_status(url, callback) {
var handle_error = function (error) {
console.log("\tFailed.\n");
console.log('\t' + error.toString().replace(/\n\r?/g, '\n\t'));
callback(false);
};
console.log("\tCalling status API...");
try {
var options = URL.parse(url);
options.timeout = 20000;
https.get(options, function (response) {
var body = '';
response.setEncoding('utf8');
response.on('data', function (data) {body += data;});
response.on('end', function () {
console.log("\tConnected.\n");
try {
var parsed = JSON.parse(body);
if ((!parsed.started || !parsed.uptime)) {
console.log('\tReceived unexpected JSON response:');
console.log('\t\t' + JSON.stringify(parsed, null, 1).replace(/\n\r?/g, '\n\t\t'));
callback(false);
}
else {
console.log('\tReceived status details from API:');
console.log('\t\tServer started:', parsed.started);
console.log('\t\tServer uptime:', parsed.uptime);
callback(true);
}
}
catch (error) {
console.log('\tReceived unexpected non-JSON response:');
console.log('\t\t' + body.trim().replace(/\n\r?/g, '\n\t\t'));
callback(false);
}
});
}).on('error', handle_error);
}
catch (error) {
handle_error(error);
}
}
If any of you can see any places where this could possibly hang without output or timeout, that'd be very helpful!
Thank you,
James Tanner
EDIT: p.s. We use https directly, instead of request so that we don't need to do any installation when the script runs. This is because the script can run on any build machine assigned to Jenkins without a custom installation.
Aren't you missing the .end()?
http.request(options, callback).end()
Something like explained here.
Inside your response callback your not checking the status..
The .on('error', handle_error); is for errors that occur connecting to the server, status code errors are those that the server responds with after a successful connection.
Normally a 200 status response is what you would expect from a successful request..
So a small mod to your http.get to handle this should do..
eg.
https.get(options, function (response) {
if (response.statusCode != 200) {
console.log('\tHTTP statusCode not 200:');
callback(false);
return; //no point going any further
}
....

handling multi clients in node.js api

Couldn't find an answer, so I'm asking here -
I'm writing an API in node.js (6.2.0) and I have a problem when I'm serving to multiple clients.
The code is -
"use strict";
const express = require('express');
const router = express.Router();
const FileRetriever = require('./FileRetriever');
function doSomething(uid, callback) {
let finalCount = 0;
let cb = null;
FileRetriever.foo(uid, function (err, data) {
finalCount = data.length;
cb = callback(finalCount);
data.forEach(function(obj, i) {
doSomething2(obj, cb);
});
})
}
function doSomething2(_obj, cb) {
let fn = null;
FileRetriever.bar(_obj, function(err, data){
cb(null, data);
})
}
router.route('/foo').get(function (req, res) {
let uid = req.query.uid;
function callback(_finalCount) {
let counter = 1;
let finalCount = _finalCount;
let output = [];
return function(err, data) {
output.push(data);
if (output.length === (finalCount -1)) {
res.send(output);
}
}
}
doSomething(uid, callback);
});
Obviously it's a bit more complicated than that, but this is the simplified version.
Please help me understand what am I missing -
This is what I have in mind on how it should work -
A user goes to /foo with a parameter uid.
He gets to this route and doSomething is invoked for this user.
In doSomething, I first get initial data that I invoke callback with that returns a function of its own which will be now known as cb.
When cb is passed to doSomething2 and get invoked there, it's still under the same stack, under the same user.
I ran a couple of tests, with one user that should return an output with the length of 6 and another with the length of 100.
When I run this code once per user, it all works fine, but if the route gets called at the same time by the two users, the lengths are not [6,100] (but they are always the same).
What am I doing wrong?
It should work...
Hope that I was clear, thanks.

How to mock event handler method using sinon.js?

I am newbie to Node.js and I am writing DAO layer for HBase which will wrap thrift and provide clear interface to other layers. I am trying to write unit tests for it using sinon.js and mocha but not sure how to ensure mock one event of Thrift connection class and its event handler.
My DAO code is as follows:
var thrift = require('thrift');
var libDirRelativePath = "../../../lib";
var hbaseThriftDirPath = libDirRelativePath + "/hbase-gen-nodejs";
var hbase = require(hbaseThriftDirPath + '/THBaseService');
var hbaseTypes = require(hbaseThriftDirPath + '/hbase_types');
var thritfPrimaryServerAddress = 'nn2';
var thritfBackupServerAddress = 'backup-nn2';
var thriftServerPort = 9090;
exports.putRows = function(tableName, putObjectArray, callback) {
var primaryClusterConnection = thrift.createConnection(thritfPrimaryServerAddress, thriftServerPort, {
transport: thrift.TBufferedTransport,
protocol : thrift.TBinaryProtocol
});
console.log('DEBUG : connection object created.');
var client = thrift.createClient(hbase, primaryClusterConnection);
console.log('DEBUG : client object created.');
primaryClusterConnection.on('connect', onConnectOfPutRows);
primaryClusterConnection.on('connect', function() {
console.log('Connected to HBase thrift server at ' + thritfPrimaryServerAddress + ":" + thriftServerPort);
client.putMultiple(tableName, putObjectArray, callback);
connection.close();
});
primaryClusterConnection.on('error', function() {
console.log('Error occurred in HBase thirft server connection.');
});
}
For above code I Just want to create stubs primaryClusterConnection and client objects which I have managed but problem is that stub of primaryClusterConnection doesn't have any idea about connect event and its handler so console.log('Connected to HBase thrift server at '... line never gets executed. I want to test that part of the code as well. Can anyone please help me in writing proper stubs/mocks for this problem?
My test code is as follows:
var hbaseDao = require('../../../src/dao/hbase/HBaseDao.js');
var libDirRelativePath = "../../../lib";
var hbaseThriftDirPath = libDirRelativePath + "/hbase-gen-nodejs";
var hbase = require(hbaseThriftDirPath + '/THBaseService');
var chai = require('chai');
var should = chai.should();
var expect = chai.expect;
var sinon = require('sinon');
describe("HBaseDao", function() {
describe(".putRows()", function() {
it("Should execute callback after inserting objects in HBase.", function(done) {
var commonStub = sinon.stub();
var connection = {
close : function() {
console.log('connection closed.');
}
};
commonStub.withArgs('nn2', 9090).returns(connection);
var client = {};
commonStub.withArgs(hbase, connection).returns(client);
var tableName = 'DUMMY_READINGS_TABLE';
var callBackMethod = function() {
console.log('dummy callback function.');
};
commonStub.withArgs(tableName, [], callBackMethod).returns(0);
hbaseDao.putRows(tableName, [], callBackMethod);
expect(hbaseDaoSpy.callCount).to.equal(1);
done();
});
Let's start by simplifying the problem a bit.
it.only("Should execute callback after inserting objects in HBase.", function(done) {
var events = require('events');
var hbaseDao = new events.EventEmitter();
hbaseDao.putRows = function() {
console.log('putting rows');
this.emit('notify');
};
hbaseDao.on('notify', function(){
console.log('notify event fired');
done(); //here's where you call the callback to assert that the event has fired
});
sinon.spy(hbaseDao, 'putRows');
var commonStub = sinon.stub();
var tableName = 'DUMMY_READINGS_TABLE';
var client = {};
var connection = {
close : function() {
console.log('connection closed.');
}
};
var callBackMethod = function() {
console.log('dummy callback function.');
};
commonStub.withArgs('nn2', 9090).returns(connection);
commonStub.withArgs({}, connection).returns(client);
commonStub.withArgs(tableName, [], callBackMethod).returns(0);
hbaseDao.putRows(tableName, [], callBackMethod);
//assertions
assert(hbaseDao.putRows.calledOnce);
});
The above test will just work, because it creates a new "hbaseDao" from a simple event emitter and has the method and the notify event ready to go.
Because we're doing an async test, we need to have the done callback in the spec. Notice that this will only fire "done" when the event has occurred. Hence, the test will not pass unless the event fires. Also notice that we're spying specifically on the the hbaseDao 'putRows' and we're asserting that the its called once, another way to ensure that the test is working. Now consider this example and apply it to your original question.
I think you almost got it, but you need to put your done callback in the callback stub as so:
var callBackMethod = function() {
console.log('dummy callback function.');
done();
};
That way, when your primaryClusterConnection.on('connect') event is fired, the supplied callback will execute the done and complete the test.
That being said, you should leave your primaryClusterConnection intact and let the implementation details of hbaseDao not be considered in your test.
You mentioned that:
primaryClusterConnection doesn't have any idea about connect
But that can't be right, because you're creating a new connection in the test and there's nothing in your implementation that tells me you have changed the event handler for the connection.
So I think in the end, you're missing the point of the test, which is simply should execute callback... and you're stubbing out stuff that you don't even need to.
Try something like this:
//use it.only to make sure there's no other tests running
it.only("Should execute callback after inserting objects in HBase.", function(done) {
//get the class
var hbaseDao = require('../../../src/dao/hbase/HBaseDao.js');
//spy on the method
sinon.spy(hbaseDao, 'putRows');
//create a table name
var tableName = 'DUMMY_READINGS_TABLE';
//create callback method with done.
var callBackMethod = function() {
console.log('dummy callback function.');
done();
};
//run the function under test
hbaseDao.putRows(tableName, [], callBackMethod);
//assert called once
assert(hbaseDao.putRows.calledOnce);
});

Node.js - Organising code and closures - SFTP/Inotify

I was hoping I could get some advice on why my nodejs program is behaving in the way it is.
I am using two modules, node-sftp and node-inotify. I have setup node-inotify to watch a directory and call a function when something is written there, the function being an sftp upload.
Now the problem I have is that processing one file at a time is fine but when I drop 4 files in one go there, the function is called four times but only one sftp upload goes through.
Do I need to order my code in a particular way to ensure that the sftp upload occurs x times, is this something to do with closures perhaps?
This is a basic version of my code...
"event_handler" is called when something happens on a "watched" directory
"check_event" figures out if this type of event is one we want, in this case it's a "write"
"ftp_to_server" prepare connection details
"do_ftp" basically uses the node-sftp module to perform the sftp upload
event_handler = function(event){
var supplier;
check_event(event, supplier, type, ftp_to_server);
};
=================
function check_event(event, handler)
{
if (event.type === 'xxxxxx') {
var file_to_process_name = 'abc';
var file_to_process_dir = 'abc';
var remote_dir = 'abc';
handler(file_to_process_name, file_to_process_dir, remote_dir);
}
}
function ftp_to_server(file_to_process_name, file_to_process_dir, remote_dir) {
var connection_details = conf.ftp.connections
do_ftp(connection_details, file_to_process_name, file_to_process_dir, remote_dir);
}
function do_ftp(connection_details, file_to_process_name, file_to_process_dir, remote_dir) {
var credentials = {
// FTP settings here
};
var local_file = file_to_process_dir + file_to_process_name;
var remote_file = remote_dir + file_to_process_name;
connection = new sftp(credentials, function(err) {
if (err){
throw err;
}
connection.writeFile(remote_file, fs.readFileSync(local_file, "utf8"), null, function(err) {
if (err) {
throw err;
}
console.info('FTP PUT DONE');
});
});
};
Your "connection = new sftp(credentials, function(err) {"
should be
var connection = new sftp(credentials, function(err) {
The way you currently have it coded, "connection" is a global and you are writing over it.

Categories