Example of how to use web-hook node for response which takes more than 20 seconds

synchronous
asynchronous
web-hook-node
redis
webhook

(Swagata Sengupta) #1

Hello Users,

Platform provides Service Nodes for calling external APIs. But it has a maximum timeout of 20 seconds.
For some APIs however it may take more than 20 seconds to respond. Though, as per industry standards, APIs are expected to have a average response times of about 6 seconds.

I want to walk you through a way you can use web-hook node and BotKit to resolve a response which could take more than 20 seconds.

Note: Web-Hook node supports a timeout of up to 10 minutes. But if you use it in synchronous way (example - FindFlight.js on our BotKit GitHub repository) Platform will still timeout in 20 seconds on Kore cloud platform.

You will need to use asynchronous way (as explained in BookACab.js on our BotKit GitHub repository)

Below is another simple working code which you can use for test.
Note - We are using setTimeout function along with service call to a delay API. setTimeout will add bulk of the time. In below example, setTimeOut waits for 30 seconds while delay API adds 5 seconds delay.

Firstly you need to have redis for this. Ensure redis is running (default port 6379)

image

Then in botkit’s config.json have the following configuration:

"redis": {
    "options": {
        "host": "localhost",
        "port": 6379
    },
    "available": true
},

Then you will need to have a js file like the below registered in app.js.
sdk.registerBot(require('./CustomBotkitWebHookNodeDemo.js'));

It is assumed that the bot will have a webhook node with name hookCallAPIFromBotkit.
Note the usage of

callback(null, new sdk.AsyncResponse());

AsyncResponse() tells the platform to wait for the response to be sent asynchronously.
and within
sdk.saveData(requestId, data)
the respond to hook will send the data back.
sdk.respondToHook(data);

In bot, based on the following code, the data from web-hook node will be available in
context.endPointResp

var botId = "st-XXXXXXX-XXX-XX";
var botName = "webhookDemoBot";
var sdk            = require("./lib/sdk");
var Promise        = sdk.Promise;
var request        = require("request");
var config         = require("./config");

//Make request to service app
    
function callAPIEndPoint(api) {
var serviceUrl = undefined;

if(api){
	serviceUrl = api;
} else {
	serviceUrl = 'https://httpbin.org/delay/5';
}
console.log("callAPIEndPoint is calling URL " + serviceUrl);

return new Promise(function(resolve, reject) {
    request({
        url: serviceUrl,
        method: 'get',
    }, function(err, res) {
        if (err) {
            return reject(err);
        }
        resolve(JSON.parse(res.body));			
	});
});


}



module.exports = {
	botId   : botId,
	botName : botName,
	 on_user_message : function(requestId, data, callback) {
		sdk.sendBotMessage(data, callback);
		console.log("###request id: " + requestId);
		console.log("on_user_message ==> " + data.message);
	},
	on_bot_message  : function(requestId, data, callback) {
		sdk.sendUserMessage(data, callback);
		console.log("###request id: " + requestId);
		console.log("on_bot_message ==> " + data.message);
	},
	on_webhook      : function(requestId, data, componentName, callback) {
		console.log("###request id: " + requestId);
		var context = data.context;
		if (componentName === 'hookCallAPIFromBotkit') {
		  var api = null;
		  console.log("on_webhook ==> calling API for hookCallAPIFromBotkit");
		console.log("wait on... " + new Date());
		sdk.saveData(requestId, data)
				.then(function() {
					setTimeout(function(){

						callAPIEndPoint(api).then(function(endPointResp) {
							data.context.endPointResp = endPointResp;
							sdk.respondToHook(data);
							console.log("wait done... " + new Date());
						});
					},30000);
					callback(null, new sdk.AsyncResponse());
				});

		}        
	},
	on_agent_transfer : function(requestId, data, callback){
		return callback(null, data);
	}
};

Output of botkit console:
image

Bot output
image

Message Node settings:
image


(Swagata Sengupta) #2

Upadate -
In certain case you may have redis server with password enabled.

You may check if you are starting redis with command like
$>redis-server /etc/redis.conf

Usually a start like
$>redis-server
does not need password.

You may get errors like NOAUTH Authentication required or ERR operation not permitted while connecting to Redis through botkit.

You will need to use the following configuration in config.json.

"redis": {
    "options": {
        "host": "<HOST>",
        "port": <PORT>,
		"password" : "<PASSWORD like MyPa$$word1 in redis.conf file against requirepass key in the file>"
    },
    "available": true
},

(Dhaval Lunagariya) #3

how can we achieve below scenario?

API call-1 -> takes 3 mins
API call-2 -> takes 0.5 mins

Both the request should process in parallel and response from call-2 should return first as it’s only taking 0.5 seconds. I tried multiple options but no luck.

cc: @shantanu.ghorai


(Swagata Sengupta) #4

Will it work for you if you split it into 2 wekbhook node calls?
How to process the calls in parallel is nodejs concept.