How to display API data using fetch on Dialogflow GUI - fetch-api

I want to display data from OMDB API on Dialogflow GUI but it's not happening. Data is being displayed fine on Google Cloud Console.
function infoHandler(agent){
let movieName = agent.parameters.movie;
agent.add(`The information for ${movieName} is as follow`);
fetch('http://www.omdbapi.com/?apikey=e255decd%20&s='+ movieName)
.then(result => result.json())
.then((json) => {
let id = json.Search[0].imdbID;
fetch('http://www.omdbapi.com/?apikey=e255decd%20&i=' + id)
.then(result => result.json())
.then((json) => {
agent.add(json.Title + json.Plot + json.imdbRatinng);
return;
}).catch((ex) => {
console.log(ex);
});
})
.catch((e) => {console.log(e);});

The issue is that fetch() causes an asynchronous operation. However, there is nothing that indicates to the Dialogflow handler dispatcher that it is asynchronous and that it should wait for it to complete before sending back a reply. To do that, you'd need to return a Promise.
Fortunately the then/catch chain that you have that are built off the fetch() return a Promise. So all you need to do is return the Promise that they have. This is as simple, in your case, by placing a return before the fetch() call. So it would look something like this:
return fetch('http://www.omdbapi.com/?apikey=e255decd%20&s='+ movieName)
// Other lines remain the same

Related

Google Action Webhook Inline Editor Returns Before the API call

This is my first Google Action project. I have a simple slot after the invocation. User enters the value on prompt and slot invokes the webhook and make a call to API using the user input. All works fine. However the webhook returns to users even before the API call finish processing and returns the value (line 1 conv.add). I do see in the logs that everything from API is logged fine after the webhook returns to user. Below is the code I am using. I am using inline editor. What am I missing? Thanks for help in advance.
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
var https = require('https');
const fetch = require('node-fetch');
const app = conversation({debug: true});
app.handle('SearchData', conv => {
const body = JSON.stringify({
val: "this is my body"
});
// prepare the header
var postheaders = {
'Content-Type' : 'application/json',
'Auth' : 'MyAuthCreds'
};
fetch('https://host.domain.com/data', {
method: 'post',
body: body,
headers: postheaders,
})
.then(res => res.json())
.then(d => {
console.log(d);
var profile = d;//JSON.parse(d);
console.log(d.entries);
console.log("Length: "+ d.entries.length);
if(d.entries.length > 0)
{
console.log("Data found");
conv.add("Data found"); //line 1
}
else
{
console.log("no data found");
conv.add("no data found"); //line 1
}
})
.catch(function (err) {
// POST failed...
console.log(err);
});
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
Your issue is that your handler is making API calls which are asynchronous, but the Assistant Conversation library doesn't know that you're doing so. So as soon as the handler finishes, it tries to send back a response, but your asynchronous responses (the stuff in the then() blocks) haven't executed yet.
To address this, you need to return a Promise object so the library knows to wait till the Promise is fulfilled before it returns.
Fortunately, in your case, this should be pretty straightforward. fetch and all the .then() blocks return a Promise. So all you need to do is add a return statement in front of the call to fetch. So something like this:
return fetch('https://host.domain.com/data', {

Intent not moving to next intent

first intent
second intent
As shown in the below code, the flow is not going from Number Intent to First Intent, it is been looped into the number loop. In dialog flow, with every intent corresponding context is also made. The flow is not moving as per context and is stuck in NumberIntent.
The flow should be like the google ask the user its survey id, the user says its id 612020 and google start asking its questions. The flow works fine until the type of question is rating i.e. user has to speak number. The error arises when the user is asked to answer in descriptive manner.
'use strict';
// Import the Dialogflow module from the Actions on Google client library.
const {dialogflow} = require('actions-on-google');
const functions = require('firebase-functions');
// Instantiate the Dialogflow client.
const app = dialogflow({debug: true});
const axios = require('axios').default;
global.ques = [];
global.i=0;
app.intent('Default Welcome Intent', (conv) => {
conv.add('Hello! What is your survey id?');
});
app.intent('NumberIntent', (conv,{number}) => {
return axios.get('https://www.openeyessurvey.com/api/get_open_survey_info/612020')
.then((result) => {
result.data.Item.QUESTIONS.map(questionobj => {
ques.push(questionobj.question);
})
conv.ask(ques[i]);
i+=1;
}).catch( err => {
console.log("error", JSON.stringify(err,null,2));
conv.close('This is not a valid survey ID');
});
});
app.intent('FirstIntent', (conv, {number}) => {
conv.ask(ques[i]);
i+=1;
});
app.intent('SecondIntent', (conv) => {
const des = conv.parameters.any;
if(des === 'ankit'){
conv.ask(ques[i]);
i+=1;
}
});
app.intent('ThirdIntent', (conv) => {
conv.ask(ques[i]);
i+=1;
});
app.intent('FourthIntent', (conv, {number}) => {
conv.ask(ques[i]);
i+=1;
});
app.intent('FifthIntent', (conv) => {
conv.ask(ques[i]);
i+=1;
conv.close('Goodbye!')
});
// Set the DialogflowApp object to handle the HTTPS POST request.
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
Output
Output2
INVALID INTENT NAME ERROR
I suspect that the issue is that i never actually gets updated.
You treat ques and i as global object, but since this is running under Firebase Cloud Functions, each call may be a new instance of the function. As a new instance, these would get reinitialized.
The flip side of this is that if you didn't get a new instance, it also has the problem that this would not work correctly if more than one person was using the Action at the same time since they would all be sharing the same value of i.
The solution to both is that, instead of storing i as a global variable, store it either in the Actions on Google session storage or in a Dialogflow context parameter.
Storing it as a session parameter, you would get the value, use it, increment it, and then save it again in the session parameter. It might look something like this:
const i = conv.data.i;
conv.ask(ques[i]);
conv.data.i = i+1;

Sails.js 1.0 helpers chaining as Promises

I'm new to Sails.js and I'm looking to develop a new application using sail.js and in this application, I want to respond to a POST request as quickly as possible, then handle a number of tasks with the payload asynchronously. Ideally I'd have a helper for each step of the tasks I want to carry out on the payload and chain them all asynchronously in the action. I've been trawling through the docs and can't seem to find a way to do this.
Is this the right way to approach this issue (if so how/can you point me to docs) or are there alternative ways to handle this issue that I have overlooked?
Thanks
With ES6, you can use helpers both with async/await or as promises.
const temp1 = await sails.helpers.stepone();
const temp2 = await sails.helpers.steptwo( temp1 );
let result = await sails.helpers.stepthree( temp2 );
// use result here
OR
sails.helpers.stepone
.then(sails.helpers.steptwo)
.then(sails.helpers.stepthree)
.then(result => {
// use result here
});
Just set up your service methods as promises and resolve early. You can import bluebird, for example, to accomplish this.
In your controller:
myPostEndpoint: (req, res) => {
return MyProcessorService.initProcessing(req.body).then(res.json);
}
And in your service MyProcessorService:
var Promise = import('bluebird');
//... other init code
module.exports = {
initProcessing: data => {
//do some validation...
// then just resolve and continue
Promise.resolve({ status: 'processing'});
return MyProcessorService.step1(data)
.then(MyProcessorService.step2)
.then(MyProcessorService.step3)//and so on....
},
step1: dataFromInit => {
//do stuff and resolve for step2
},
step2: dataFromStep1 => {
//do stuff and resolve for step3
},
step3: dataFromStep2 => {
//do stuff and resolve
},
//and so on
}
You could also set up a worker queue with something like Bull and Redis to send off jobs to and run in a WorkerService or separate worker app.

Asynchronous Request to Facebook API with Redux

I am using Redux and trying to make a call to Facebook API with their JS SDK. I've only ever used promises with Redux and so since the method FB.getLoginStatus just returns a simple JS object, I'm not sure how to ensure that the payload doesn't return undefined.
With redux-promise, you add it to the applyMiddleware(ReduxPromise)... and then it ensures nothing is returned until the promise resolves. But I don't know how to do that here.
I've also used async/await functions with React Native without an issue, but I tried using them here and for some reason the code still returns the payload, before the asynchronous request (await ...) is finished. So I tried working with redux-await, but couldn't get it to work.
export function getLoginStatus() {
var res = FB.getLoginStatus(function(res) {
console.log(res);
});
console.log("res ", res);
return {
type: GET_LOGIN_STATUS,
payload: res
}
}
Hm, things can get a little tricky as I've not used redux-promise. And I can't tell exactly what else you have tried. But this would be my first shot:
async function _getLoginStatus() {
var payload = new Promise( (resolve, fail) => {
FB.getLoginStatus((res)=>resolve(res));
});
return {
type: GET_LOGIN_STATUS,
payload: payload
}
}
// Last time I exported an async function I needed this HYMMV
export let getLoginStatus = _getLoginStatus;
And then elsewhere in the code:
import {getLoginStatus} from 'whatever.js';
var payloadResult = await getLoginStatus();

Intern test framework - Making a REST API call

I am trying to make a REST API call as below and once the call is complete, I want to print "Done".
But with the below example "Done" is getting printed even before the REST call is complete.
return this.remote
.then(function() {
request('http://www.google.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body) // Show the HTML for the Google homepage.
}
})
})
.then(function() {
console.log("Done")
})
Am I missing something here? If this not the right way, could someone please let me know what the right way is.
Thanks.
For Leadfoot (the library that Intern uses to drive functional tests) to keep track of an asynchronous operation, asynchronous operations in then callbacks should return Promises (or thenables). Luckily, request returns a promise, so just do:
.then(function () {
return request('...', function (...) {
...
});
})