Dialogflow-fulfillment-nodejs: Issue with callback/promise in handler functions

Created on 28 Feb 2018  路  6Comments  路  Source: dialogflow/dialogflow-fulfillment-nodejs

Thanks your work on this, I've found it much easier to use than other AoG/DialogFlow libraries despite being in early beta.

With the latest update (removing builders), there's a problem with callback/promises in handler functions, specifically when doing HTTP requests. I believe it is a combination of the removal of agent.send() and the way HTTP client libraries do callback (possibly not preserving 'this' reference as needed?).

Something like this works as expected:

function myFunc (callback) {
    // Do something
    callback();
}
function anotherFunc() {
    agent.add('My Response');
}
myFunc(anotherFunc);

However, if you try this (request library, callback)

request('http://www.google.com', (error, response, body) => {
  agent.add('Hello');
});

Or with axios (Promise-based)

axios.get('https://google.com')
    .then(response => {
        agent.add('My response');
        })
    .catch (error => {
        // do something
    })

You get the following error

Error: No responses defined for null
    at V1Agent.sendResponse_ (/user_code/node_modules/dialogflow-fulfillment/v1-agent.js:119:13)
    at WebhookClient.send_ (/user_code/node_modules/dialogflow-fulfillment/dialogflow-fulfillment.js:225:19)
    at promise.then (/user_code/node_modules/dialogflow-fulfillment/dialogflow-fulfillment.js:285:38)
    at process._tickDomainCallback (internal/process/next_tick.js:135:7)

I've played around with this a bit and I'm not sure what exactly the issue is. It seems related to how these HTTP clients handle callback and losing the 'this' reference, obviously both request and axios are very popular HTTP clients. An easy fix seems like adding back agent.send(), but I'm not sure if the plan is to remove that.

Most helpful comment

function handlers support promises now so you can return a promise and process things like http requests in the promise. Here is an example of using the request library:

function dialogflowHanlderWithRequest(agent) {
  return new Promise((resolve, reject) => {
    request.get(options, (error, response, body) => {
      JSON.parse(body)
      // processing code
      agent.add(...)
      resolve();
    });
  });
};

You can also move the HTTP call to another function that returns a promise. Here is an example with the axios library:

function dialogflowHandlerWithAxios(agent) {
  return callApi('www.google.com').then(response => {
    agent.add('My response');
  }).catch (error => {
    // do something
  })
};

function callApi(url) {
    return axios.get(url);
}

Does this satisfy your use case?

All 6 comments

function handlers support promises now so you can return a promise and process things like http requests in the promise. Here is an example of using the request library:

function dialogflowHanlderWithRequest(agent) {
  return new Promise((resolve, reject) => {
    request.get(options, (error, response, body) => {
      JSON.parse(body)
      // processing code
      agent.add(...)
      resolve();
    });
  });
};

You can also move the HTTP call to another function that returns a promise. Here is an example with the axios library:

function dialogflowHandlerWithAxios(agent) {
  return callApi('www.google.com').then(response => {
    agent.add('My response');
  }).catch (error => {
    // do something
  })
};

function callApi(url) {
    return axios.get(url);
}

Does this satisfy your use case?

Thank you - this clears up my issues! Keep up the great work on this.

Thanks! will do. I'll add a sample/documentation demonstrating this at some point.

Hello Matthewayne,

Subject: Dialogflow Fulfillment

Few days earlier i was able to execute the code properly and the output was returned to the bot.
But now i am not getting any response as on bot.The logs from firebase show that API Call is successful and the response is returned correctly. But unable to share the result with user.

Code is as follow.

function getLatLng(agent) {
let city = agent.parameters['geo-city'];
url=url+city;
//agent.add(url);

callWeatherApi(city,agent).then(() => {
    //agent.add( output ); // Return the results of the weather API to Dialogflow
}).catch(() => {
    //res.json(`I don't know the weather but I hope it's good!` );
});
agent.add(`Welcome to the Weather Function!`);
//agent.add(`I can Show Weather Latitude & Longitude`);

}

function callWeatherApi (city,agent) {
return new Promise((resolve, reject) => {

console.log('API Request: ' + url);

// Make the HTTP request to get the weather
console.log('Starting http Call');
http.get(url, (res) => {
     console.log('Inside http Call 1');
  let body = ''; // var to store the response chunks
  var bodyChunks = [];
  res.on('data', (d) => { body += d;}); // store each response chunk
  res.on('end', () => {
    // After all the data has been received parse the JSON for desired data
    let response = JSON.parse(body);
    console.log(response);
    let lat=response.results[0].geometry.location.lat;
    let lon=response.results[0].geometry.location.lng;
    console.log('Current Latitude is'+lat+'  '+lon);



    let output='Current Latitude & Longitude for'+city+' is '+lat+'  /  '+lon;
    agent.add(output);
    // Resolve the promise with the output text
    console.log(output);
    resolve();
  });
  res.on('error', (error) => {
    console.log(`Error calling the weather API: ${error}`);
    reject();
  });
});
  console.log('Finished http Call');

});
}

Your help would be appreciated, Thank you !!!

Did you fix the problem? I'm with the same.

Inside of request, method agent.add now working.

agent.add( JSON.stringify(body))
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

function dialogflowHanlderWithRequest(agent) {
console.log("dialogflowHanlderWithRequest");
agent.add("dialogflowHanlderWithRequest()");

var request = require('request');
var jsonX = {} ;
jsonX.requisicao = "aaaaaaa";
jsonX.page_id = "7777777";

return new Promise((resolve, reject) => {
request.post({
url: 'https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
body:jsonX,
json: true
}, function(error, response, body){
console.log(JSON.stringify(body));
agent.add( JSON.stringify(body));
resolve();

});

});
}

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gakuba picture gakuba  路  8Comments

kwizfreak picture kwizfreak  路  9Comments

kylebegovich picture kylebegovich  路  7Comments

malikasinger1 picture malikasinger1  路  4Comments

mukuljainx picture mukuljainx  路  5Comments