Running Async functions in Microsoft Bot Builder (with Node.JS) - javascript

I'm trying to make a test bot that, upon being chatted to responds with a (meaningless) string gotten from a JSON object through another API
Code:
var restify = require('restify');
var builder = require('botbuilder');
var request = require('request-promise');
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
// Create chat connector for communicating with the Bot Framework Service
var connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
// Listen for messages from users
server.post('/api/messages', connector.listen());
// Receive messages from the user and respond by echoing each message back (prefixed with 'You said:')
var bot = new builder.UniversalBot(connector, function (session) {
var text = await MyRequest()
session.send("%s", text);
});
async function MyRequest() {
var options = {
uri: "https://jsonplaceholder.typicode.com/posts/1",
method: "GET",
json: true
}
try {
var result = await request(options);
return result;
} catch (err) {
console.error(err);
}
}
The problem is the bot var isn't an asynch function, so I can't put await in it. If I remove await, the bot replies with Object Promise. I'm fairly inexperienced in JS overall, so can I get any pointers?
e: The Request part works great, I've tested it alone in a different js program

Have you tried this. If you are using ES6 compatible Node environment this should work
var bot = new builder.UniversalBot(connector, async function (session) {
// Use JSON.stringify() if MyRequest Promise will resolve a object
var text = await MyRequest()
session.send("%s", text);
});

If async/await isn't possible, how about returning a promise? like below:
function MyRequest() {
var options = {
uri: "https://jsonplaceholder.typicode.com/posts/1",
method: "GET",
json: true
}
return request(options);
}
And use Promise.then to act on the result, like so:
var bot = new builder.UniversalBot(connector, function (session) {
MyRequest().then(function(text) {
session.send("%s", text);
}).catch(function(error) {
session.send("%s", error);
});
});

Related

How to prevent async - await freezing in javascript?

Good day I have a custom adonisjs command that pulls from an API.
async handle (args, options) {
// Status
// Open = 1979
// Get all jobs with open status.
const pullJobController = new PullJobsFromJobAdderController;
let token = await pullJobController.get_token();
if(token){
const jobs = await this._getOpenJobs('https://jobs/open-jobs', token , 1979);
}
}
async _getOpenJobs(url, accessToken, status) {
url = url + '?statusId=' + status
const headers = {
'Authorization': 'Bearer ' + accessToken
}
const options = {
method: 'GET',
url: url,
headers: headers
}
return (await rp(options).then(function (result) {
return {
status: true,
info: JSON.parse(result)
}
}).catch(function (error) {
return {
status: false
}
}));
} // _getOpenJobs()
PullJobsFromJobAdderController
async get_token()
{
// This works if directly returning the token.
// return "9ade34acxxa4265fxx4b5x6ss7fs61ez";
const settings = await this.settings();
const jobAdderObject = new this.JobAdder(settings.jobadder['client.id'], settings.jobadder['client.secret'])
const jobadderOauthObject = this.model('JobadderOauth');
const accessInfo = await jobadderOauthObject.jobdderLatestAccess();
let isAccessExpired = await this.checkAccessValidity(accessInfo.created_at);
let accessToken = accessInfo.access_token;
let apiEndpoint = accessInfo.api_endpoint;
if(isAccessExpired === true){
let refreshTokenInfo = await jobAdderObject.refrehToken(accessInfo.refresh_token)
if (refreshTokenInfo.status === true) {
let refreshTokenDetails = JSON.parse(refreshTokenInfo.info)
accessToken = refreshTokenDetails.access_token
apiEndpoint = refreshTokenDetails.api
await jobadderOauthObject.create({
code: accessInfo.code,
access_token: refreshTokenDetails.access_token,
refresh_token: refreshTokenDetails.refresh_token,
scope: 'read write offline_access',
api_endpoint: refreshTokenDetails.api
})
}
}
return accessToken;
} // get_token()
The function async get_token works as expected, it supplies me with a fresh token to be used by the adonisjs command. However it freezes after running the command.
But if I return the string token directly. The custom command handle() works as expected and terminates after running.
Scenario 1: (Directly returning the token string from PullJobsFromJobAdderController)
I run my custom command "adonis pull:jobs" and it runs as expected displaying in the terminal the result of the pulled data from the api.
Terminal is ready to accept another command.
Scenario 2: (Comment out the directly returned string token from PullJobsFromJobAdderController)
I run my custom command "adonis pull:jobs" and it runs as expected
displaying in the terminal the result of the pulled data from the
api.
Terminal is not accepting commands until I press ctrl+c and terminate the current job/command.
Perhaps I am missing something regarding async await calls.
Can someone point / help me to the right direction?
TIA
I got it, for anyone else having this kind of problem with adonis commands:
wrap the task inside your handle in a try... catch block then always have Database.close() and process.exit() in finally.

Returning a value from async function to outer function Node js [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I am fairly new to the whole asynchronous world. Had to start for implementing Twilio video call through node.
I have been trying to call this server side function which then calls another asynchronous function that returns promise. The then portion works fine and I am able to see output in console. But when I try to call another function in then which has an ajax call to the main application to save some data in database.
Neither am I able to send the returned value in response.send() nor am I able to call a function in then.
Please help!
Server Side - index.js
'use strict';
/**
* Load Twilio configuration from .env config file - the following environment
* variables should be set:
* process.env.TWILIO_ACCOUNT_SID
* process.env.TWILIO_API_KEY
* process.env.TWILIO_API_SECRET
*/
require('dotenv').load();
const express = require('express');
const http = require('http');
const path = require('path');
const { jwt: { AccessToken } } = require('twilio');
const Twilio = require('twilio');
const jquery = require( 'jQuery');
const cors = require('cors');
const VideoGrant = AccessToken.VideoGrant;
// Max. period that a Participant is allowed to be in a Room (currently 14400 seconds or 4 hours)
const MAX_ALLOWED_SESSION_DURATION = 14400;
// Create Express webapp.
const app = express();
// Set up the path for the HealthAssure.
const startPath = path.join(__dirname, '../HAVideoConsultation/public');
app.use('/HAVideoConsultation', express.static(startPath));
/**
* Default to the application.
*/
app.get('/', (request, response) => {
response.redirect('/HAVideoConsultation');
});
/**
* Generate an Access Token for a chat application user - it generates a random
* username for the client requesting a token, and takes a device ID as a query
* parameter.
*/
app.get('/token', function(request, response) {
const { identity } = request.query;
// Create an access token which we will sign and return to the client,
// containing the grant we just created.
const token = new AccessToken(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_API_KEY,
process.env.TWILIO_API_SECRET,
{ ttl: MAX_ALLOWED_SESSION_DURATION }
);
// Assign the generated identity to the token.
token.identity = identity;
// Grant the access token Twilio Video capabilities.
const grant = new VideoGrant();
token.addGrant(grant);
// Serialize the token to a JWT string.
response.send(token.toJwt());
});
function pushCompositionId(compositionId){
console.log(compositionId);
jquery.ajax({
url:'http://localhost:58674/ABC/XYZ',
type:'GET',
data: {CompositionId:compositionId},
cors: true,
success:function(result){
Console.log('Composition Id pushed successfully.');
},
error:function(err){
console.log(err);
return false;
}
});
}
app.get('/Composition',function(request,response){
const client = new Twilio(process.env.TWILIO_API_KEY,process.env.TWILIO_API_SECRET, {accountSid: process.env.TWILIO_ACCOUNT_SID});
const cid = null;
client.video.compositions
.create({
roomSid: request.query.roomSid,
audioSources: '*',
videoLayout: {
grid : {
video_sources: ['*']
}
},
format: 'mp4'
}).then(composition =>{
console.log("Created Composition with SID=" + composition.sid); // This works properly
cid=composition.sid;
// pushCompositionId(composition.sid); // I want to call this function here
});
response.send(cid); // This does not return proper value
});
// Create http server and run it.
const server = http.createServer(app);
const port = process.env.PORT || 3000;
server.listen(port, function() {
console.log('Express server running on *:' + port);
});
Client side
async function GenerateCompositionId(roomsid){
const compositionid = await fetch(`/Composition?roomSid=${roomsid}`);
}
Server Side function I want to call in then after Composition.sid is generated. If I put this in a try block it gives me error jquery.ajax is not a function. I have included it require and another ajax function on client side is working fine. Why does this not?
function pushCompositionId(compositionId){
jquery.ajax({
url:'http://localhost:58674/ABC/XYZ',
type:'GET',
data: {CompositionId:compositionId},
cors: true,
success:function(result){
Console.log('Composition Id pushed successfully.');
},
error:function(err){
console.log(err);
return false;
}
});
}
In order to send the response when the async calls are done the server-side must be:
var jsdom = require("jsdom");
const { JSDOM } = jsdom;
const { window } = new JSDOM();
const { document } = (new JSDOM('')).window;
global.document = document;
var $ = jQuery = require('jquery')(window);
function pushCompositionId(compositionId,response){
console.log(compositionId);
jquery.ajax({
url:'http://localhost:58674/ABC/XYZ',
type:'GET',
data: {CompositionId:compositionId},
cors: true,
success:function(result){
Console.log('Composition Id pushed successfully.');
response.send(cid); // This does not return proper value
},
error:function(err){
console.log(err);
response.send(false);
}
});
}
app.get('/Composition',function(request,response){
const client = new Twilio(process.env.TWILIO_API_KEY,process.env.TWILIO_API_SECRET, {accountSid: process.env.TWILIO_ACCOUNT_SID});
const cid = null;
client.video.compositions
.create({
roomSid: request.query.roomSid,
audioSources: '*',
videoLayout: {
grid : {
video_sources: ['*']
}
},
format: 'mp4'
}).then(composition =>{
console.log("Created Composition with SID=" + composition.sid); // This works properly
cid=composition.sid;
pushCompositionId(composition.sid,response); // I want to call this function here
});
});
Otherwise you can use await/async to return a sync-like result

OneSignal & Firebase Cloud Function - Returned undefined, expected Promise or value

I'm trying to use cloud function to trigger a push notification whenever a new chat message document is created in Firestore.
I'm also using OneSignal as shown in the code below. However, cloud function keeps complaining my function doesn't return a promise or value and I'm not sure what function to return here. Where and what should I return?
exports.newChatNotification = functions.firestore.
document('channels/{userId}/thread/{docId}').
onCreate((snap, context) => {
// Get the values of the newly created document
const newValue = snap.data();
// Access the "content" field
const content = newValue.content;
console.log(content);
// Access the "userID" field
const userID = newValue.userID;
console.log(userID);
const docId = context.params.docId;
console.log(docId);
// OneSignal notification sending
var sendNotification = function(data) {
var headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "{Authorization token}"
};
var options = {
host: "onesignal.com",
port: 443,
path: "/api/v1/notifications",
method: "POST",
headers: headers
};
var https = require('https');
var req = https.request(options, function(res) {
res.on('data', function(data) {
console.log("Response:");
console.log(JSON.parse(data));
});
});
req.on('error', function(e) {
console.log("ERROR:");
console.log(e);
});
req.write(JSON.stringify(data));
req.end();
};
var message = {
app_id: "APP_ID",
contents: {"en": content},
include_player_ids: ["PLAYER_ID_1", "PLAYER_ID_2"]
};
sendNotification(message);
});
Cloud Functions requires you to return a promise that resolves when all the asynchronous work is complete in your function. That's how you tell Cloud Functions that it's safe to clean up and terminate the function. This is not optional - if you don't return a promise correctly like this, the work might never complete, and your code will simply not work.
I suggest reading the documentation about this, and be sure to understand how JavaScript promises work.

AWS Kendra sdk call will not return results

I have been following the AWS-Kendra react-search app example you can find here:
https://docs.aws.amazon.com/kendra/latest/dg/deploying.html
After importing the Kendra client with:
const kendra = require('aws-sdk/clients/kendra');
const kendraClient = new kendra({apiVersion: '2019-02-03', region: 'us-east-1'});
Any call on kendraClient to any of the kendra services returns null. I have been executing queries with:
const results = kendraClient.query({ IndexId: INDEX_ID, QueryText: queryText});
Which returns a request object with null data and error fields.
I have calls to S3 which execute correctly in the same file so I do not believe it to be an authentication problem. If I had to guess it's some issue with how I created the kendra object and client, the usual
kendra = new AWS.Kendra();
doesn't work because Kendra is not part of the browser version of the SDK.
Are you trying to run js from browser directly? Here is a sample nodejs code
var kendra = require("aws-sdk/clients/kendra");
var kendraClient = new kendra({apiVersion: "2019-02-03", region: "us-west-2"});
exports.handler = function (event) {
try{
console.log("Starting....");
var params = {
IndexId: "<<Enter your indexId here>>",
QueryText: "<<Enter your queryText here>>",
QueryResultTypeFilter: "DOCUMENT",
PageNumber: 1
};
var kendraResponse = kendraClient.query(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log("Kendra result is", data); // successful response
});
const response = {
"dialogAction":
{
"fulfillmentState":"Fulfilled",
"type":"Close","message":
{
"contentType":"PlainText"
}
}
}
return response;
} catch (error) {
console.log(error)
}
};

Why is my asynch mongodb query function hanging?

First of all, please forgive me if this is a duplicate, I am new to coding and Javascript in general.
I have an async function that queries mongodb based on an objects passed in the function call. The function executes, and returns the results to a callback function which logs the results to the console, and then hangs. Ultimately, I want to take the results of the async query and then do something with them outside the original async function. I am not understanding why it hangs after it logs to the console.
const MongoClient = require('mongodb').MongoClient;
let fObj = {
field : {},
limit : 100
}
let cObj = {
dbName : 'myNewDatabase',
colName : 'newCollection'
}
async function findDoc(cObj,fObj) {
const url = 'mongodb://localhost:27017';
const client = new MongoClient(url, { useNewUrlParser: true });
try {
await client.connect();
const db = client.db(cObj.dbName);
const col = db.collection(cObj.colName);
console.log(`Connection Made to ${db.databaseName} database.`);
return await col.find(fObj.field).limit(fObj.limit).toArray();
client.close();
} catch (err) {
console.log(err.stack);
}
};
findDoc(cObj,fObj).then(function(result) {
console.log(result);
});
The code executes, and logs the results to the console, but then hangs. I have to ctrl-c out to get it to end. What am I missing?
I suppouse you're running your code with NodeJs. This implies that you have a promise hanging up, which keeps the server running. I assume this is because your connection to the DB is still open after you have found the document.
You need to move your client.close(); statement above the return statement, because it is never reached otherwise and your server will hang up forever.
Your code will look like this in the end:
const MongoClient = require('mongodb').MongoClient;
let fObj = {
field : {},
limit : 100
}
let cObj = {
dbName : 'myNewDatabase',
colName : 'newCollection'
}
async function findDoc(cObj,fObj) {
const url = 'mongodb://localhost:27017';
const client = new MongoClient(url, { useNewUrlParser: true });
try {
await client.connect();
const db = client.db(cObj.dbName);
const col = db.collection(cObj.colName);
console.log(`Connection Made to ${db.databaseName} database.`);
const result = await col.find(fObj.field).limit(fObj.limit).toArray();
client.close();
return result;
} catch (err) {
console.log(err.stack);
}
};
findDoc(cObj,fObj).then(function(result) {
console.log(result);
});
Also, I advise you to enclose your whole async function's body into the try clause. This way you will be able to effectively intercept any error. Imagine your new MongoClient failed to instantiate - you would end up with an uncaught error inside a promise, which isn't very nice.

Categories