Async functions in meteor server - javascript

I'm using vpulim:node-soap to have a soap server running.
My meteor server startup contains this amongst various other code:
authRequestOperation: function(args,cb,headers,req) {
console.log(args);
var authResponceObject = {};
var futureAuthResponse = new Future();
Fiber(function(){
if(collectorUsers.findOne({username: args.username})){
console.log("Found User");
authResponceObject = {
username: args.username,
nonce: Random.id()
};
console.log("authResponceObject is: " + JSON.stringify(authResponceObject,null,4));
console.log("futureAuthResponse returning...");
futureAuthResponse.return(authResponceObject);
}
// console.log("futureAuthResponse waiting...");
// return futureAuthResponse.wait();
}).run();
console.log("authResponceObject after fiber is: " + JSON.stringify(authResponceObject,null,4));
return authResponceObject;
},
What I'm trying to do is:
I receive a user object from client.
I check if the user is present in the mongodb
If user is present, prepare response object
Respond to the client with the response object.
I have 1. working. However, the dute it being async call, the order of 2,3,4 is messed up.
Right now what's happening is:
receive client object
return response object (which is empty)
Check mongo
Prepare response object.
I'm not using Meteor.methods for the above.
How do I make this work in the right manner? I've tried juggling around wrapAsync and fiber/future but hitting dead ends.

I believe Meteor.bindEnvironment can solve your problem, try this code:
{
// ...
authRequestOperation: Meteor.bindEnvironment(function(args, cb, headers, req) {
console.log(args);
var authResponceObject = {};
if (collectorUsers.findOne({username: args.username})) {
console.log("Found User");
authResponceObject = {
username: args.username,
nonce: Random.id()
};
console.log("authResponceObject is: " + JSON.stringify(authResponceObject, null, 4));
}
return authResponceObject;
}),
// ...
}

Related

In Node, how to execute sql from global database connection

I am unable to execute the sql, when using the global database connection in node.js.
I have followed the steps as in Azure documentation: https://learn.microsoft.com/en-us/azure/mysql/connect-nodejs and able to display the output on the console. But, I want to put all my Azure SQL database connection in a separate file, but the select query is not printing the output on the console.
DatabaseManager.js
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var sqlConnection = function sqlConnection() {
// Create connection to database
var config =
{
userName: 'uname',
password: 'password',
server: 'dbserver.database.windows.net',
options:
{
database: 'mydatabase',
encrypt: true
}
}
var connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on('connect', function(err) {
if (err)
{
console.log(err)
}
else
{
console.log('CONNECTED TO DATABASE');
}
}
);
}
module.exports = sqlConnection;
app.js
var restify = require('restify');
var builder = require('botbuilder');
var botbuilder_azure = require("botbuilder-azure");
var azure = require('azure-storage');
var dbconnection = require('./DatabaseManager');
bot.dialog('profileDialog',
(session) => {
session.send('You reached the profile intent. You said \'%s\'.', session.message.text);
console.log('Reading rows from the Table...');
dbconnection("select FNAME from StudentProfile where ID=1"),
function (err, result, fields) {
if (err) throw err;
console.log(result);
}
session.endDialog();
}
Console Output:
Reading rows from the Table...
CONNECTED TO DATABASE
I was expecting the output of FNAME, but nothing is printing on the console. Is there anything, I am missing?
Thank you.
There's a couple of problems here. First off, you should only ever import a module once per file. This is just a performance consideration and won't actually break your code.
Next, pay attention to what you're exporting from your DatabaseManager module. Right now, you're exporting a function that creates the connection and then doesn't do anything with it. We can fix this by using a pattern called a "callback" which lets us provide a function that will then be called with the connection as an argument.
I added a ton of comments to the code explaining things. This code won't run as-is - there's a couple places where I have "do this or this". You'll have to choose one.
var Tedious = require('tedious'); // Only require a library once per file
var Connection = Tedious.Connection;
var Request = Tedious.Request;
// Or using the object spread operator
var { Connection, Request } = require('tedious');
// You called this `sqlConnection`. I'm going to use a verb since it's a
// function and not a variable containing the connection. I'm also going
// to change the declaration syntax to be clearer.
function connect(cb) { // cb is short for callback. It should be a function.
var config = {
userName: 'uname',
password: 'password',
server: 'dbserver.database.windows.net',
options: {
database: 'mydatabase',
encrypt: true
}
}; // Put a semi-colon on your variable assignments
var connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on('connect', function(err) {
if (err) {
console.log(err);
return; // Stop executing the function if it failed
}
// We don't need an "else" because of the return statement above
console.log('CONNECTED TO DATABASE');
// We have a connection, now let's do something with it. Call the
// callback and pass it the connection.
cb(connection);
});
}
module.exports = connect; // This exports a function that creates the connection
Then back in your main file, you can use it like so.
var restify = require('restify');
var builder = require('botbuilder');
var botbuilder_azure = require('botbuilder-azure');
var azure = require('azure-storage');
var connect = require('./DatabaseManager'); // renamed to be a verb since it's a function.
bot.dialog('profileDialog', (session) => { // Hey, this is a callback too!
session.send('You reached the profile intent. You said \'%s\'.', session.message.text);
console.log('Creating a connection');
connect((connection) => {
// or with the traditional function notation
connect(function(connection) {
console.log('Reading rows from the Table...');
// Execute your queries here using your connection. This code is
// taken from
// https://github.com/tediousjs/tedious/blob/master/examples/minimal.js
request = new Request("select FNAME from StudentProfile where ID=1", function(err, rowCount) { // Look another callback!
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
});
request.on('row', function(columns) { // Iterate through the rows using a callback
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
console.log(column.value);
}
});
});
connection.execSql(request);
});

Watson Conversation - Oracle DB Integration

Good morning/afternoon all, I am trying to get Watson to return a response manually set from our Oracle Databases.
I am using async to sequentially access the database and return the response because the first problem I had was the DB query would not happen until after Watson had already returned a response. Async.waterfall fixes this.
My current problem: In the console I see everything logged correctly. The Database is queried, response.output.text is set correctly and then returned to Watson but no response ever appears in my chat.. If I set the response.output.text = "asdfa" before the async, asdfa is returned as expected.
I am at a loss trying to figure this out so I appreciate any and all help. Please let me know if any more information is required.
// Send the input to the conversation service
conversation.message(payload, function (err, data) {
if (err) {
return res.status(err.code || 500).json(err);
}
return res.json(updateMessage(payload, data));
});
});
function updateMessage(input, response) {
var responseText = null;
if (!response.output) {
response.output = {};
} else {
// checkNames check
if (response.output.nodes_visited[0] === 'slot_11_1519333387192' && response.entities[0].entity === 'confirm') {
/* This code actually returns asdfa as a response from Watson.. */
// response.output.text = "asdfa";
// return response;
async.waterfall([
// this function queries the database
// TODO: module out the Oracle connection parts once POC is completed
function queryDB(callback) {
console.log('Starting queryDB');
var query = "SELECT column_name FROM table#prod WHERE column_id = '" + response.context.VSUID + "'";
oracledb.getConnection('hr',
function (err, connection) {
var conn = oracleGetConnection(err, connection);
conn.execute(query, {}, {
outFormat: oracledb.OBJECT
},
function (err, result) {
console.log('result from Oracle: ', result);
// pass a null error and the result of the query
callback(null, result.rows[0]);
});
});
},
// this function formats the result of the query
// TODO: this should not be it's own function. This can happen at the same time the db gets the row
function formatName (arg1, callback) {
console.log('this should happen after query..');
console.log('arg1: ', arg1);
var r = JSON.stringify(arg1);
r = r.substring((r.indexOf(':') + 1) + 1, r.length - 2);
console.log('Name is: ', r);
// pass a null error and the formatted name
callback(null, r);
}
],
// Final function to be ran after the two above have completed
function finalFunction (err, result) {
if (err) {
console.log('uh oh async err: ', err);
} else {
console.log('This is final Function');
// set output text
response.output.text = 'Is your name ' + result + '?';
// response.context.dbResponse = 'Is your name ' + result + '?';
// response.output.text = "asdfasdfasd";
// console.log('This is the value of response\n\n', response);
// var resp = returnResponse(input, response);
response.context.dbResponse = response.output.text[0];
return returnResponse(input, response);
// return response;
}
});
// response.output.text = "asdfa";
console.log('This is response.output.text ', response.output.text);
return response;
} else {
//If no special if case to query the db just run Watson Conversation stock
return returnResponse(input, response);
}
}
}
Here is a sample console log.
This logs the Input from the user:
name 111111111
This logs the Response from Watson:
Is 111111111correct?
This logs the intent recognized, if any:
nameCheck
This logs the entity recognized, if any:
VSUID
This logs the text that is being returned to the user: [ 'Is 111111111correct?'
]
Starting queryDB
Connected to database
result from Oracle: { outBinds: undefined,
rowsAffected: undefined,
metaData: [ { name: 'TABLE_FIRST_NAME' } ],
rows: [ [ 'Tyler' ], [ 'Tyler' ] ],
resultSet: undefined }
this should happen after query..
arg1: [ 'Tyler' ]
Name is: "Tyler
This is final Function
This logs the Input from the user:
yes
This logs the Response from Watson:
Is your name "Tyler?
This logs the entity recognized, if any:
confirm
This logs the text that is being returned to the user: Is your name "Tyler?
Writing code like the following will open you up to SQL injection vulnerabilities (and likely performance problems too):
var query = "SELECT column_name FROM table#prod WHERE column_id = '" + response.context.VSUID + "'";
Please read the section of the documentation on bind variables.
On to your question...
You are treating updateMessage as though it is a synchronous function, but it is performing asynchronous work. A function that is performing asynchronous work will need an asynchronous API, such as Node.js style callbacks, Promises, or AsyncFunctions (async/await).
If you see line 73 of the code you supplied, you're "returning" the response object, but that is outside of the async.waterfall call. Even the return on line 67 will not work because of the asynchronous nature of Node.js.
Here's my latest attempt at describing how all of this works:
https://www.youtube.com/watch?v=iAdeljxq_hs
You can access the slides and sample code here:
https://www.dropbox.com/s/quu7oxiug0gh6ua/Understanding%20Async%20Processing%20and%20Patterns%20in%20Node.js.zip?dl=0
In the sample code's code > header-detail directory, you'll see 5 different files that start with header-detail-with- followed but the name of a different API choice you could make. You will have to make a similar choice with your updateMessage API.
To run a test, use the ddl.sql file to create the target tables, then edit db-config.js as needed for your environment, and finally run node test.js 1 from a terminal in that directory. You can change the number at the end to run a different test file.

alexa Steam custom skill api integration

currently trying to develop one of my first alexa skills. trying to build a skill that updates the user on who out of their Steam friends, are online.
Curerntly it's pretty messy and not in the format i hope it to be in the end, but for testing purposes, I'm not yet using intents, im just testing using the launchrequest.
So far I've managed to get a users (mine) friends list from steam and put all the friends ids in a url that should be able to grab the details for these
users.
The issue I'm having is performing the second API call to grab the players details using the steamIds. i keep getting an 'undefined' return and am stumped on what i'm doing wrong.
I'm very much new to JS so there's bound to be mistakes in here, but i can work on tidying up later once i've got it working.
This works fine
/**
* Called when the user invokes the skill without specifying what they want.
*/
function onLaunch(launchRequest, session, callback) {
console.log("onLaunch requestId=" + launchRequest.requestId
+ ", sessionId=" + session.sessionId);
var cardTitle = "Hello, World!"
testGet(function (response) {
var speechOutput = "Here is the result of your query: " + response;
var shouldEndSession = true;
callback(session.attributes,
buildSpeechletResponse(cardTitle, speechOutput, "", true));
});
//var speechOutput = "You can tell Hello, World! to say Hello, World!"
//callback(session.attributes,
// buildSpeechletResponse(cardTitle, speechOutput, "", true));
}
This is the function that is grabbing the details from my friendlist
function testGet(response) {
var http = require('http')
var url = " http://api.steampowered.com/ISteamUser/GetFriendList/v0001/?key=XXXXXXXXXX&steamid=76561198068311091&relationship=friend"
http.get(url, function (res) {
// data is streamed in chunks from the server
// so we have to handle the "data" event
var buffer = "",
data,
friendsList,
i,
address,
textResponse,
route;
res.on("data", function (chunk) {
buffer += chunk;
});
res.on("end", function (err) {
// finished transferring data
// dump the raw data
console.log(buffer);
console.log("\n");
data = JSON.parse(buffer);
friendsList = data.friendslist.friends;
textResponse = isOnline(friendsList);
response("Friends online: " + textResponse);
}).on('error', function (e) {
console.log("Error message: " + e.message);
});
})
}
and this is the final function which i'm having difficulties with.
function isOnline(friendsList){
var http = require('http'),
i,
comma,
friendsIDs = "";
// for loop to get all friends ids in string
for (i = 0; i < friendsList.length; i++) {
// if i equals 0 then it is the start of the loop so no
//comma needed, otherwise add a comma to seperate the ids.
if(i === 0) {comma = ""}
else{comma = ","}
//place the ids in a comma seperate string
friendsIDs += comma + friendsList[i].steamid;
}
var playerurl = "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=xxxxx&steamids=" + friendsIDs;
// works fine up to this point
// run the api call to get player details
http.get(playerurl, function (response) {
// data is streamed in chunks from the server
// so we have to handle the "data" event
var buffer = "",
playerdata,
returnText,
textResponse,
friendsInformation,
route;
response.on("playerdata", function (chunk) {
buffer += chunk;
});
response.on("end", function (err) {
// finished transferring data
// dump the raw data
console.log(buffer);
console.log("\n");
playerdata = JSON.parse(buffer);
friendsInformation = playerdata.response.players;
for (i = 0; i < friendsInformation.length; i++) {
if(friendsInformation[i].personastate == 1) {
returnText += friendsInformation[i].personaname + " chicken";
}
}
return returnText;
}).on('error', function (e) {
console.log("Error message: " + e.message);
});
});
}
Been going round in circles for hours and feel so close to doing this but have no idea where I'm going wrong?!
thanks
I have managed to solve my problem by using javascript promises. I'm completely new to promises so took some trial and error but have managed to get it to work. here is the simplest video i could find to explain the concept, it definately helped me understand how to rearrange my code.
If you wish to do two API calls in an Alexa skill, using the data from the first api call to construct and inform the second call, you will need to use promises to do them sequentially.
it took the code from the isOnline() and testGet() functions and moved them into a promise, which allowed me to complete 1 api call (the original call to get friends list info) before executing the second call (the second api to get player details based on the results of the friendslist api call)
I can now check with alexa to see which of my steam friends are online! Hopefully i'll be able to build in some more functionality (eg what they are playing, if they are offline or just away/busy/snoozing)
thanks for contributing

Variable not updating properly

This is the relevant code :
This sends a request to the server, takes an userName as an argument.
function send_CLIENT_LOOKUP(userName)
{
send(
auth.AOCP.CLIENT_LOOKUP,
[ [ 'S', userName.toString() ]
]
)
}
This part handles the response from the server.
handle[auth.AOCP.CLIENT_LOOKUP] = function (data, u)
{
console.log('CLIENT_LOOKUP')
var userId = u.I()
var userName = u.S()
u.done()
console.log({ userId : userId, userName : userName }) // this works properly
var idResult = userId; // I can actually use the userID but I just assigned it to idResult to make the code easier to understand.
}
The above 2 functions work how they are supposed to, nothing to change/fix there.
Now I have a function that receives a request from one user and sends a request to another user, it takes 2 arguments: first arg is the userId of the user that sends the request and the second is the userName of user the request will be sent to however the server/game only works with userIds so the userName has to be converted:
var commands = {
invite: function(userId, userName) {
send_CLIENT_LOOKUP(userName); // send a request to the server to find out the userId of the userName
send_PRIVGRP_INVITE(idResult);
}
}
The problem is that idResult == undefined, unless I call cmd.invite() again then idResult == 'with the previous response like this:
cmd.invite(user1);
cmd.invite(user2);
cmd.invite(user3);
And the output of idResult is:
idResult == undefined
idResult == 'info for user1'
idResult == 'info for user2'
I tried defining idResult outside response handler and update it inside, to make sure its not some sort of delay from the server I just did a massive invite spam and the result was the same, 1 step behind no matter how fast I sent the invites. Any suggestions are welcome :)
The problem is rather hard to solve, as sending and collecting replies are isolated. It seems that send() sends a packet and handle() receives a reply, and there can be many possible unrelated packets between sending and receiving.
If it's the case then a special global map should be created to link requests and responses. For cases when there are 2 concurrent requests for the same username, EventEmitter from events module is a good fit, as it's essentially a multimap of strings to callbacks:
var events = require('events')
var outstandingLookups = new events.EventEmitter()
So when you send a lookup you register a one time listener, so there's no need for manual cleanup:
var commands = {
invite: function(userName) {
outstandingLookups.once(userName, function (idResult)
{
send_PRIVGRP_INVITE(idResult);
})
// send a request to the server to find out the userId of the userName
send_CLIENT_LOOKUP(userName);
}
}
In response handler emit conveniently calls for you all the callbacks expecting userId for the received userName:
handle[auth.AOCP.CLIENT_LOOKUP] = function (data, u)
{
console.log('CLIENT_LOOKUP')
var userId = u.I()
var userName = u.S()
u.done()
console.log({ userId : userId, userName : userName }) // this works properly
// I can actually use the userID but I just assigned it to idResult
// to make the code easier to understand.
var idResult = userId;
// this is the only addition to original handler code
outstandingLookups.emit(userName, idResult)
}
The need to use outstandingLookups.once is actually an implementation detail and it should be properly encapsulated/hidden if you want to lookup userIds in more than one place in the code.
I will use promises from q npm library as I remember the interface, but similar ES6 promises should be used in production code as they are a modern standard.
var Q = require('q')
var commands = {
lookupUserName: function (userName)
{
var result = Q.defer()
outstandingLookups.once(userName, function (idResult)
{
result.resolve(idResult);
})
// send a request to the server to find out the userId of the userName
send_CLIENT_LOOKUP(userName);
return result.promise
},
invite: function(userName) {
commands.lookupUserName(userName).then(function (idResult)
{
send_PRIVGRP_INVITE(idResult);
})
}
}
The code for commands.invite is much cleaner now.
It looks like you're doing an async-request but updating the var idResult synchronously.
So you should put the call to send_PRIVGRP_INVITE(idResult) into the callback or response handler of send_CLIENT_LOOKUP(userName)

node.js + socket.io + multiple database calls needed for result

I need help.
I've been trying to wrap my head around async programming with node.js and socket.io for a day now. I understand that I need some flow control but I don't seem to understand how to properly implement it.
I have a redis datastore that has modules stored in a set let's say 'moda','modb'
instances of those modules are 'moda:instances' and in 'modb:instances' the properties of those instances are stored in 'moda:instancea' and 'modb:instanceb' as a hash.
I am trying to get the following json:
"moda": {"instancea": {"property1": "value1", "property2", "value2"}}, "modb": {"instanceb": {"property1": "value1"}}
Could someone give me a little push in the right direction?
Here is my current code:
var io = require('socket.io').listen(2000);
var redis = require('redis').createClient();
var http = require('http');
var async = require('async');
var step = require('step');
io.sockets.on('connection', function (socket) {
var notifications = require('redis').createClient();
notifications.subscribe("notification");
notifications.on("message", function (channel, message) {
socket.send(message);
console.log(channel + ':' + message);
});
socket.on('modules', function(params, callback) {
var response = {};
async.series([
function (callback) {
console.log('1>');
redis.smembers('modules', function (err, modules) {
async.forEachSeries(modules, function(module, moduleCallback) {
response[module] = {}
redis.smembers(module + ':instances', function(err, instances) {
async.forEachSeries(instances, function(instance, instanceCallback) {
response[module][instance] = {}
console.log('2>' + module + ':' +instance);
instanceCallback();
});
moduleCallback();
});
});
callback();
});
},
function (callback) {
console.log('3');
callback();
}
], function() {
console.log(JSON.stringify(response));
});
});
});
The output from this code is:
info - socket.io started
debug - client authorized
info - handshake authorized JMMn1I8aiOMGCMPOhC11
debug - setting request GET /socket.io/1/websocket/JMMn1I8aiOMGCMPOhC11
debug - set heartbeat interval for client JMMn1I8aiOMGCMPOhC11
debug - client authorized for
debug - websocket writing 1::
1>
3
{"moda":{}}
2>moda:instancea
2>moda:instanceb
2>modb:instancea
The problem comes from the fact forEachSeries requires an additional callback as a third parameter (to be called when the whole processing is complete). You cannot just put some code after forEachSeries hoping it will be called once it is complete.
Here is your code modified:
var response = {};
async.series([
function (callback) {
console.log('1>');
redis.smembers('modules', function (err, modules) {
async.forEachSeries(modules, function(module, moduleCallback) {
response[module] = {}
redis.smembers(module + ':instances', function(err, instances) {
async.forEachSeries(instances, function(instance, instanceCallback) {
response[module][instance] = {}
console.log('2>' + module + ':' +instance);
instanceCallback();
}, moduleCallback );
});
}, callback );
});
},
function (callback) {
console.log('3');
callback();
}],
function() {
console.log(JSON.stringify(response));
});
Note how the callback, and moduleCallback are used as a third parameter. The output is:
1>
2>moda:instancea
2>moda:instanceb
2>modb:instancea
3
{"moda":{"instancea":{},"instanceb":{}},"modb":{"instancea":{}}}
which I guess is what you expected.
Additional remark: forEachSeries will process everything in sequence, the next operation waiting for the previous one to complete. This will generate plenty of roundtrips to Redis. forEach should be much more efficient here to leverage pipelining.
Take a look also at promise concept, with promises you configure the flow much more readable way.
First you need to prepare promise versions of redis methods that you're using:
var promisify = require('deferred').promisify;
var RedisClient = require('redis').RedisClient;
RedisClient.prototype.psmembers = promisify(RedisClient.prototype.smembers);
Then you can construct your flow as:
console.log('1>');
redis.psmembers('modules').map(function (module) {
response[module] = {};
return redis.psmembers(module + ':instances').map(function (instance) {
response[module][instance] = {};
console.log('2>' + module + ':' +instance);
});
}).end(function () {
console.log('3');
console.log(JSON.stringify(response));
});
Check: https://github.com/medikoo/deferred

Categories

Resources