I am calling a javascript function , which in turn calls a web service;The response of this service is used to call another function which also calls a service. At end of both services we set session attributes. This code gives no errors, but the callback gets called before the service has returned data. The main motive of this code is to set the session attributes before return of flow from this code, when the callback gets called before the service has returned values the session attributes are not set and the requirement of the code is not fulfilled.
'use strict';
function close(sessionAttributes, fulfillmentState, message) {
return {
sessionAttributes,
dialogAction: {
type: 'Close',
fulfillmentState,
message : 'For security purpose answer these questions '
},
};
}
function getSecurityQuestions(intentRequest, context, post_options, callback){
const sessionAttributes = intentRequest.sessionAttributes || {};
var policynumber = sessionAttributes.policynumber;
var interactionID = sessionAttributes.interactionID;
var body = "";
var body2;
const http = require('https');
const promise = new Promise((resolve, reject) => {
const post_data = JSON.stringify({"Purpose":"SecurityQuestions", "InteractionID":interactionID, "SearchStringAcctNum":policynumber});
//ignores SSL
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var post_request = http.request(post_options, function(res) {
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
context.done(body);
resolve(body);
});
res.on('error', function(e) {
reject(Error(e.message));
context.fail('error:' + e.message);
});
});
// post the data
post_request.write(post_data);
post_request.end();
});
callback( promise.then((body) => {
body2 = JSON.parse(body);
sessionAttributes.question1 = body2.SecurityDetails[0].Question;
close(sessionAttributes, 'Fulfilled');
}, (error) => {
console.log(error.message);
})
);
}
function getInteraction(intentRequest, context, callback) {
const slots = intentRequest.currentIntent.slots;
var policynumber = "PA"+slots.PolicyNumber;
var questionOne = slots.questionOne;
var questionTwo = slots.questionTwo;
const sessionAttributes = intentRequest.sessionAttributes || {};
console.log("policy number : "+policynumber + "question 1 : "+questionOne + "question 2 : "+questionTwo);
sessionAttributes.policynumber = policynumber;
var body = "";
var body2;
// An object of options to indicate where to post to
var post_options = {
host: 'example.com',
protocol: 'https:',
port: '3000',
path: '/hiddenPath',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
const http = require('https');
const promise = new Promise((resolve, reject) => {
const post_data = JSON.stringify({"Purpose":"CreateInteraction"});
//ignores SSL
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var post_request = http.request(post_options, function(res) {
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
context.done(body);
resolve(body);
});
res.on('error', function(e) {
console.log("rejected here");
reject(Error(e.message));
context.fail('error:' + e.message);
});
});
// post the data
post_request.write(post_data);
post_request.end();
});
callback( promise.then((body) => {
body2 = JSON.parse(body);
console.log("interaction ID : "+body2.InteractionID);
sessionAttributes.interactionID = body2.InteractionID;
getSecurityQuestions(intentRequest, context, post_options, callback);
}, (error) => {
console.log('Promise rejected.');
console.log(error.message);
}));
}
// --------------- Intents -----------------------
/**
* Called when the user specifies an intent for this skill.
*/
function dispatch(intentRequest, context, callback) {
const intentName = intentRequest.currentIntent.name;
if (intentName === 'currIntent') {
return getInteraction(intentRequest, context, callback);
}
throw new Error(`Intent with name ${intentName} not supported`);
}
// --------------- Main handler -----------------------
function loggingCallback(response, originalCallback) {
console.log("logging callback called......");
originalCallback(null, response);
}
exports.handler = (event, context, callback) => {
try {
dispatch(event, context, (response) => loggingCallback(response, callback));
} catch (err) {
callback(err);
}
};
You should resolve your promise only after the request ends.. Have updated your sample below. Hope it helps. Also, you were sending an invalid object as your post body. Fixed that as well.
function getValue(context, post_options, callback) {
var body = "";
var body2;
const http = require('http');
const promise = new Promise((resolve, reject) => {
// INVALID OBJECT
//const post_data = JSON.stringify({"something"});
const post_data = JSON.stringify({
something: "something"
});
//ignores SSL
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var post_request = http.request(post_options, function(res) {
res.on('data', function(chunk) {
body += chunk;
console.log("inside " + JSON.stringify(body));
// DONT RESOLVE HERE, REQUEST IS NOT COMPLETE
//resolve(body);
});
res.on('end', function() {
context.done(body);
//RESOLVE HERE INSTEAD
resolve(body);
});
res.on('error', function(e) {
reject(Error(e.message));
context.fail('error:' + e.message);
});
});
// post the data
post_request.write(post_data);
post_request.end();
});
promise.then((body) => {
console.log("response data " + JSON.stringify(body));
body2 = JSON.parse(body);
callback(delegate(sessionAttributes, intentRequest.currentIntent.slots));
}, (error) => {
console.log('Promise rejected.');
console.log(error.message);
});
}
Related
I would like to use two IBM Watson services and combine the responses from both in one variable and return it as a callback. I couldn't figure out how to get response1 value outside the http request to combine it with response2 from the other IBM Watson service.
I tried the below code and it didn't work. I read that I can use promises, but I'm pretty new to this, and couldn't figure out how to do this.
Can anyone help please?
const AWS = require('aws-sdk');
var http = require('https');
exports.handler = (event, context, callback) => {
var text = JSON.stringify(event.text);
var options = {
method: process.env.method,
hostname: process.env.watson_hostname,
port: null,
path: process.env.path,
headers: {
'content-type': process.env.content_type,
authorization: process.env.authorization,
'cache-control': process.env.cache_control,
'X-Watson-Learning-Opt-Out': 'true'
}
};
var req = http.request(options, function (res) {
var chunks = "";
res.on("data", function (chunk) {
chunks+ = chunk.toString();;
});
res.on("end", function () {
var response1 = (chunks);
//////////////here I need to get reponse2
var response2 = IBM2();
var bothResponses = response1 + response2
callback(null,bothResponses)
});
})
req.write(text);
req.end()
function IBM2(){
var text = JSON.stringify(event.text);
var options = {
method: process.env.method2,
hostname: process.env.watson_hostname2,
port: null,
path: process.env.path2,
headers: {
'content-type': process.env.content_type2,
authorization: process.env.authorization2,
'cache-control': process.env.cache_control,
'X-Watson-Learning-Opt-Out': 'true'
}
};
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var response2 = JSON.parse(Buffer.concat(chunks));
return(response2)
}
Return a promise from your IBM2 function and handle using async await in the calling function. Notice the async keyword before on end callback.
I have tried to add Promise to your existing flow:
const AWS = require('aws-sdk');
var http = require('https');
exports.handler = (event, context, callback) => {
var text = JSON.stringify(event.text);
var options = {
method: process.env.method,
hostname: process.env.watson_hostname,
port: null,
path: process.env.path,
headers: {
'content-type': process.env.content_type,
authorization: process.env.authorization,
'cache-control': process.env.cache_control,
'X-Watson-Learning-Opt-Out': 'true'
}
};
var req = http.request(options, function (res) {
var chunks = "";
res.on("data", function (chunk) {
chunks += chunk.toString();;
});
res.on("end", async function () {
var response1 = (chunks);
//////////////here I need to get reponse2
var response2 = await IBM2();
// validate response2 (in case IBM2 throws error)
var bothResponses = response1 + response2;
callback(null,bothResponses)
});
});
req.write(text);
req.end();
function IBM2(){
return new Promise((resolve, reject) =>{
var text = JSON.stringify(event.text);
var options = {
method: process.env.method2,
hostname: process.env.watson_hostname2,
port: null,
path: process.env.path2,
headers: {
'content-type': process.env.content_type2,
authorization: process.env.authorization2,
'cache-control': process.env.cache_control,
'X-Watson-Learning-Opt-Out': 'true'
}
};
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var response2 = JSON.parse(Buffer.concat(chunks));
resolve(response2)
});
res.on("error", function (err) {
reject(err);
});
})
});
}
};
Before making any changes to your code, I would suggest you go thru these topics first.
For reference, do take a look into the concept of promises and async-await
Promiese
Async/Await
Dont know if you have other errors, but it looks like youre not waiting for response2 to finish, maybe something like
const response2 = await IBM2():
or if you want to use promises maybe something like:
res.on('end', function () {
var response2 = IBM2().then(
val => {
var bothResponses = response1 + val;
callback(null, bothResponses);
},
reject => {
/* handle rejection here */
},
);
});
I'm trying to retrieve the response(var body) from response_handler function to my /image/search route. But the problem is I cannot do it by making it (var body) a global variable since it's asynchronous.
router.get('/imagesearch/:term', (req, res) => {
let term = req.params.term;
bing_web_search(term);
res.json('trying to add json response here')
});
let host = 'api.cognitive.microsoft.com';
let path = '/bing/v7.0/search';
let response_handler = function (response) {
let body = '';
response.on('data', function (d) {
body += d;
});
response.on('end', function () {
console.log('\nRelevant Headers:\n');
for (var header in response.headers)
// header keys are lower-cased by Node.js
if (header.startsWith("bingapis-") || header.startsWith("x-msedge-"))
console.log(header + ": " + response.headers[header]);
body = JSON.stringify(JSON.parse(body), null, ' ');
console.log('\nJSON Response:\n');
console.log(body);
});
response.on('error', function (e) {
console.log('Error: ' + e.message);
});
};
let bing_web_search = function (search) {
console.log('Searching the Web for: ' + search);
let request_params = {
method : 'GET',
hostname : host,
path : path + '?q=' + encodeURIComponent(search),
headers : {
'Ocp-Apim-Subscription-Key' : subscriptionKey,
}
};
let req = https.request(request_params, response_handler);
req.end();
}
My node js app is making multiple http request if there is delay in response of say 20 secs. Below is the sample code for the same.
First I make call to the getAPI function from browser. getApi function calls the getAccessToken API, after receiving the accesstoken I am calling the testApi. Now if there is a delay of 20 secs in response from testApi then getAccessToken Api is getting called again. I don't want to use promise. Can anyone point out what I am missing or doing wrong here?
shttp = require('http-https');
exports.getAPI = function(typeObj, request, response, callback) {
var userConf; //contains info such as port, host, url etc
_this.getAccessToken(function(tokenResponse) {
var tokenInfo = JSON.parse(tokenResponse);
var accessToken = JSON.parse(tokenInfo.response);
accessToken = accessToken.access_token;
if(accessToken) {
_this.testApi(userConf,accessToken,function(sjmResponse) {
callback(sjmResponse);
}
} else {
callback(JSON.stringify({"payLoad":null,"reasonCode":"fail","status":null}));
}
});
};
exports.getAccessToken = function(cb) {
var tokenConf; //contains info such as port, host, url etc
var httpReq = shttp.request(tokenConf, function(res) {
res.setEncoding('utf8');
if (res.statusCode == 200) {
var body = "";
res.on('data', function (result) {
body += result;
});
res.on('end', function (){
cb(JSON.stringify({error: '', response: (body)}));
});
} else {
cb(JSON.stringify({error: 'Failed to get user access token '+res.statusCode, response:''}));
}
});
httpReq.on('error', function(e) {
cb(JSON.stringify({error: 'Failed to get user access token'+e, response:''}));
});
httpReq.end();
};
exports.testApi = function(userConf,accessToken,sjmCallback) {
var userConf; //contains info such as port, host, url etc
var httpSubmitReq = shttp.request(userConf, function(res) {
res.setEncoding('utf8');
if (res.statusCode == 200) {
var body = "";
res.on('data', function (result) {
body += result;
});
res.on('end', function () {
sjmCallback(body);
});
} else {
sjmCallback(JSON.stringify({"payLoad":null,"reasonCode":"fail","status":null}));
}
});
httpSubmitReq.on('error', function(e) {
sjmCallback(JSON.stringify({"payLoad":null,"reasonCode":"fail","status":null}));
});
httpSubmitReq.end();
};
app.get('/testApi', function (req, res) {
var typeObj = {};
typeObj.apiType= 'testApi';
try {
getAPI(JSON.stringify(typeObj), req, res, function(response) {
res.end(response);
});
} catch(err) {
res.end(err);
}
});
Below I am trying to repond to a GET with the weather in JSON format. I am attempting the use the const done in order to deliver the response. This does not seem to work. I get the response and weather in console but return nothing to the client.
'use strict';
console.log('Loading function');
const doc = require('dynamodb-doc');
const http = require('http');
const dynamo = new doc.DynamoDB();
function get_json(url, callback) {
http.get(url, function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
var response = JSON.parse(body);
callback(response);
});
});
}
exports.handler = (event, context, callback) => {
const done = (err, res) => callback(null, {
statusCode: err ? '400' : '200',
body: err ? err.message : JSON.stringify(res),
headers: {
'Content-Type': 'application/json',
},
});
switch (event.httpMethod) {
case 'DELETE':
dynamo.deleteItem(JSON.parse(event.body), done)
break;
case 'GET':
// dynamo.scan({ TableName: event.queryStringParameters.TableName }, done);
done(get_json("http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=92f14e06a6652e81a5a58bd13d152f70", callback, function (resp) {
callback(resp);
}));
break;
}
};
get_json() doesn't return anything, so passing it into done is definitely the wrong thing to do.
It looks like you need to do this:
get_json(
"http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=92f14e06a6652e81a5a58bd13d152f70",
resp => done(null, resp)
);
I am new to node js and I am trying to use async module to eliminate the setTimeouts. Here I am facing a problem. It is not working as expected. It calls the second function even before the first function completes execution. I searched for answers and tried multiple ways. But it doesn't seem to work. It prints "Inside db insert in async series" even before the async.forEach finishes. Can anyone please check the code and tell me where I'm going wrong?
setTimeout(function() {
async.series([function(callback1) {
console.log("Inside async series");
try {
var msg = "";
var datas = [];
for (var i = 0; i < service_name.length; i++) {
console.log("Inside for loop service names");
var child = {
"space_guid": space_guid,
"name": service_name[i],
"service_plan_guid": service_plan_guid[i]
};
datas.push(child);
console.log("datas array===" + JSON.stringify(datas))
}
async.forEach(datas, function(data1, callback) {
console.log("Inside async task");
var data = JSON.stringify(data1);
console.log("data value===" + JSON.stringify(data));
var options = {
host: 'api.ng.bluemix.net',
path: '/v2/service_instances' +
'?accepts_incomplete=true',
method: 'POST',
headers: {
'Authorization': full_token_new
}
};
console.log("options is" + JSON.stringify(options));
var reqst = http.request(options, function(res) {
console.log("Sent for request");
res.setEncoding('utf8');
res.on('data', function(chunk) {
msg += chunk;
});
res.on('end', function() {
try {
console.log("message =======", msg);
console.log("-----------------------------------------");
msg = JSON.stringify(msg);
msg1 = JSON.parse(msg);
console.log("printing msg--" + msg1);
console.log("-----------------------------------------");
console.log("here i am", i);
console.log(service_name.length - 1);
callback();
} catch (err) {
console.log(err);
}
});
});
reqst.on('error', function(e) {
console.log(e);
});
reqst.write(data);
reqst.end();
}, function(err) {
console.log("for each error" + err);
});
callback1(null, null);
} catch (err) {
console.log(err);
}
},
function(callback1) {
console.log("Inside db insert in async series")
db_insert(service_name, solnName, full_token_new, uname, version);
callback1(null, null);
}
],
function(err, results) {
if (err) {
console.log("There's an error" + err);
} else {
console.log("result of async", results);
}
})
}, 3000)
You are mixing try...catch with asynchronous code, this is bad practice and almost impossible to do right.
Also, your error stem from the fact you are calling callback just after async.forEach, which don't finish, and go to the next step.
Also, what do you mean by "eliminate the timeout"? Your whole code is in it, you can remove it whenever you want.
'use strict';
async.series([
(callback) => {
let msg = "",
datas = [],
i = 0;
while(i < service_name.length) {
let child = {
"space_guid": space_guid,
"name": service_name[i],
"service_plan_guid": service_plan_guid[i]
};
datas.push(child);
i = i + 1;
}
async.forEach(datas, (data1, callback) => {
let data = JSON.stringify(data1),
options = {
host: 'api.ng.bluemix.net',
path: '/v2/service_instances?accepts_incomplete=true',
method: 'POST',
headers: {
'Authorization': full_token_new
}
},
reqst = http.request(options, (res) => {
res.setEncoding('utf8');
res.on('data', (chunk) => {
msg += chunk;
});
res.on('end', () => {
msg = JSON.stringify(msg);
msg1 = JSON.parse(msg);
callback();
});
});
reqst.on('error', (error) => {
callback(error);
});
reqst.write(data);
reqst.end();
}, (error) => {
callback(error);
});
},
(callback) => {
db_insert(service_name, solnName, full_token_new, uname, version);
callback();
}
],
(error, results) => {
if (error) {
console.log("There's an error" + error);
} else {
console.log("result of async", results);
}
});
Since this smell heavily like a plssendzecode question, I've removed every console.log and gone ES6 to make sure you will not be able to use it as such and need to read the change I made.
I simplify code a little.
datas and processData aren't good names.
setTimeout(onTimer, 3000);
function onTimer() {
var datas = service_name.map(function(name, i) {
return {
space_guid: space_guid,
name: name,
service_plan_guid: service_plan_guid[i]
}
});
function processData(data, callback) {
var options = {
host: 'api.ng.bluemix.net',
path: '/v2/service_instances?accepts_incomplete=true',
method: 'POST',
headers: {
'Authorization': full_token_new
}
};
var reqst = http.request(options, function(res) {
var msg = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
msg += chunk;
});
res.on('end', function() {
try {
msg = JSON.parse(msg);
callback(null, msg);
} catch (err) {
callback(err);
}
});
});
reqst.on('error', callback);
reqst.write(JSON.stringify(data));
reqst.end();
}
async.map(datas, processData, function(err, results) {
if (err);
return console.log(err);
// process msg of each request db_insert(...);
});
};