Sending dynamic data in response using agent.add() - dialogflow-es

My requirement is to send dynamic data with response using Dialogflow’s Nodejs fulfillment library beta.
My query is to know the possibility of sending dynamic data with response which we got that from external api ? There are cases where we need to send real time data to dialogflow agent ( in turn to client) by calling a API . I tried below code to implement this but its not working , here " value" is the data that we are getting from api response and the same has been parsed / navigated to extract json value.
agent.add(stock price is + JSON.parse(value).IRXML.StockQuotes.Stock_Quote[0].Trade)
Prior above code execution , i'm making a synchronous external API call using promotion so that the result is replaced in the agent.add section.
At present, i'm not able to execute agent.add function inside promotion then function. Please do assist me to implement this or let me know is there any alternative approaches to do this.
Im making a external api call to fetch data so that i can sent same result back to agent. I could able to make call but i'm really not sure how to pass this received data to agent. As you can see in the below code , im calling externalCall() to make api call and this function is called from SpineDay(). The result returned from externalCall() is replaced in SpineDay(), still the result is not passed to agent using agent.add(). Any instruction agent.add() with in "externalCall(req,res).then" is not working.
function SpineDay(agent){
externalCall(req,res).then((output,agent) => {
//res.json({ 'fulfillmentText': JSON.parse(output).IRXML.StockQuotes.Stock_Quote[0].Trade });
agent.add(new Card({
title: output,
imageUrl: 'https://dialogflow.com/images/api_home_laptop.svg',
text: `This is the body text of a card. You can even use line\n breaks and emoji!`,
buttonText: 'Se rendre sur XXX',
buttonUrl: 'https://XXX/'
})
);
agent.add(new Suggestion(`Quelles sont les dernière sorties à la demande?`));
agent.add(new Suggestion(`Quel est le programme de ce soir?`));
}).catch(() => {
res.json({ 'fulfillmentText': `Error calling the weather API!` });
});
/*agent.add("Quelles sont les dernière sorties à la demande?");
agent.add("`Quel est le programme de ce soir?");*/
}
var externalCall = function(mainreq, mainres) {
return new Promise(function(resolve, reject){
// Create the path for the HTTP request to get the weather
var jsonObject = JSON.stringify(mainreq.body);
const agent = new WebhookClient({request: req, response: res});
console.log('In side post code' + Buffer.byteLength(jsonObject, 'utf8'))
// An object of options to indicate where to post to
var options = {
port: '443',
uri: 'https://xxxx.xxx.xxx//ticker/quote/stock?compid=22323&reqtype=quotes',
method: 'POST',
headers: {
'Content-Length' : Buffer.byteLength(jsonObject, 'utf8'),
'X-MDT-API-KEY': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
};
// Make the HTTP request to get the weather
request(options, function(err, res, body) {
if (!err && res.statusCode == '200'){
// After all the data has been received parse the JSON for desired
// data
let response = JSON.parse(body).IRXML.StockQuotes.Stock_Quote[0].Trade;
// Create response
let output = "xxxx stock price is " +response;
// Resolve the promise with the output text
console.log(body);
resolve(output,agent);
}else{
console.log(`Error calling the weather API: ${error}`)
reject("Error calling the weather API");
}
});
});
}

The problem is that your SpineDay function is making an asyc call, but not returning a Promise. Although externalCall is (correctly) using Promises, SpineDay isn't. The library assumes that, if you make an async call, you will return a Promise that will be fullfilled when the response is ready to send.
Fortunately, you can do that with a few additional lines in your function that returns a promise overall and returns a Promise as part of the promise chain.
function SpineDay(agent){
return externalCall(req,res)
.then((output,agent) => {
agent.add(new Card({
title: output,
imageUrl: 'https://dialogflow.com/images/api_home_laptop.svg',
text: `This is the body text of a card. You can even use line\n breaks and emoji!`,
buttonText: 'Se rendre sur XXX',
buttonUrl: 'https://XXX/'
})
);
agent.add(new Suggestion(`Quelles sont les dernière sorties à la demande?`));
agent.add(new Suggestion(`Quel est le programme de ce soir?`));
return Promise.resolve();
}).catch(() => {
res.json({ 'fulfillmentText': `Error calling the weather API!` });
return Promise.resolve();
});
}
So first, you need to return the result of the .then().catch() chain, which will be a Promise. Then, inside the chain you also need to return the result, which is best done as a Promise.

Related

Keep variable between promises in a function?

On firebase function I need to get data from Paypal and do 4 things :
1. returns an empty HTTP 200 to them.
2. send the complete message back to PayPal using `HTTPS POST`.
3. get back "VERIFIED" message from Paypal.
4. *** write something to my Firebase database only here.
What I do now works but i am having a problem with (4).
exports.contentServer = functions.https.onRequest((request, response) => {
....
let options = {
method: 'POST',
uri: "https://ipnpb.sandbox.paypal.com/cgi-bin/webscr",
body: verificationBody
};
// ** say 200 to paypal
response.status(200).end();
// ** send POST to paypal back using npm request-promise
return rp(options).then(body => {
if (body === "VERIFIED") {
//*** problem is here!
return admin.firestore().collection('Users').add({request.body}).then(writeResult => {return console.log("Request completed");});
}
return console.log("Request completed");
})
.catch(error => {
return console.log(error);
})
As you can see when I get final VERIFIED from Paypal I try to write to the db with admin.firestore().collection('Users')..
I get a warning on compile :
Avoid nesting promises
for the write line.
How and where should I put this write at that stage of the promise ?
I understand that this HTTPS Cloud Function is called from Paypal.
By doing response.status(200).end(); at the beginning of your HTTP Cloud Function you are terminating it, as explained in the doc:
Important: Make sure that all HTTP functions terminate properly. By
terminating functions correctly, you can avoid excessive charges from
functions that run for too long. Terminate HTTP functions with
res.redirect(), res.send(), or res.end().
This means that in most cases the rest of the code will not be executed at all or the function will be terminated in the middle of the asynchronous work (i.e. the rp() or the add() methods)
You should send the response to the caller only when all the asynchronous work is finished. The following should work:
exports.contentServer = functions.https.onRequest((request, response) => {
let options = {
method: 'POST',
uri: "https://ipnpb.sandbox.paypal.com/cgi-bin/webscr",
body: verificationBody
};
// ** send POST to paypal back using npm request-promise
return rp(options)
.then(body => {
if (body === "VERIFIED") {
//*** problem is here!
return admin.firestore().collection('Users').add({ body: request.body });
} else {
console.log("Body is not verified");
throw new Error("Body is not verified");
}
})
.then(docReference => {
console.log("Request completed");
response.send({ result: 'ok' }); //Or any other object, or empty
})
.catch(error => {
console.log(error);
response.status(500).send(error);
});
});
I would suggest you watch the official Video Series on Cloud Functions from Doug Stevenson (https://firebase.google.com/docs/functions/video-series/) and in particular the first video on Promises titled "Learn JavaScript Promises (Pt.1) with HTTP Triggers in Cloud Functions".

LOADPAGE - Parsing JSON Request failed, status 200

[REGISTER SHIFT/ ASSIGNMENT FORM]
Here is my form, let me describe it; it register next week working hours, I design there are 2 cases: add new and edit in the same form.
When user select an employee, if not register shift yet, we let user register for this employee, if shift is registered already, user can edit in the same form. And I think it will be better not refresh the page, every time user change employee, the form just update and let user add/edit then submit it by post method.
I searched the web, and found a recommendation for ajax/jQuery.
Any more recommendations for me? I've just learn Nodejs/Express with PostgreSQL database.
I am trying to use ajax to load mypage from post event, I call error function in ajax to see what the error is and get:
Parsing JSON Request failed. Status 200.
I'm using NodeJS Express Server, EJS view engine, body-parser, postgresql db.
pool.connect((err, client, release) => {
if (err) {
return console.error('Error acquiring client', err.stack)
}
client.query(
'SELECT * FROM "Employee"', (err, result) => {
release()
if (err) {
res.end();
return console.error('Error executing query', err.stack);
}
console.log(typeof(result));
res.type('json');
res.render("index", {emplist : result});
res.end();
})
})
My ajax function:
$.ajax({
url: "/addshift",
type: "POST",
data: JSON.stringify(employee),
dataType: "application/json",
contentType: 'application/json',
complete: function () {
console.log("go into complete !");
},
success: function (response) {
console.log(response);
console.log("go into success !");
},
error:function(x,e) {
if (x.status==0) {
alert('You are offline!!\n Please Check Your Network.');
} else if(x.status==404) {
alert('Requested URL not found.');
} else if(x.status==500) {
alert('Internel Server Error.');
} else if(e=='parsererror') {
alert('Error.\nParsing JSON Request failed. ' + x.status);
} else if(e=='timeout'){
alert('Request Time out.');
} else {
alert('Unknow Error.\n'+x.responseText);
}
}
});
let's see:
"I am trying to use ajax to load mypage from post event"
Ok, so I suppose you want to get a fully formed HTML page from your $post.
Then, I see:
console.log(typeof(result));
res.type('json');
res.render("index", {emplist : result});
res.end();
res.render will return HTML, this is good for your goal. BUT, you're also specifying a JSON type with res.type. This is causing the error. HTML is not JSON clearly.
Furthermore, you don't need the call to res.end(). res.render() will finish the transaction correctly on its own, res.end is for errors or unexpected conditions.
Your ajax code is ok, but if you're trying to update an html component, like a select, you need to do that manually using the response from ajax, like so:
$("#selectElem").html(response);
Furthermore, you should check your result object from the SELECT * FROM EMPLOYEE query is correctly formatted as proper JSON

Capture a JSON GET response in a variable?

I'm using request-promise to get data from an endpoint that I have.
Is it posible to 'capture' a json response in a variable to use it anywhere?
try{
var gamer = '';//variable to capture json data
var options = {
uri: 'http://localhost:4000/gamers/'+gamer._id+'/find',
json: true
};
RequestPromise(options)
.then(function (data) {
gamer = data;//capturing response
})
.catch(function (err) {
console.log("Error saving player data !");
});
.... do something with gamer ....
}catch(err){
res.status(500).send({
message: err.message || 'An error occurred generating player teams !'
});
}
The reason that I need to do this is because actually I don't have access to the database to get that information, so my only option is to consume an API to get information through collections id's.
Your doing a lot of things correctly already. The issue is your gamer variable will get assigned the value you expect first when your promise resolves. There are many ways to skin this cat, but to get you started try performing whatever you want to perform on the gamer variable in .then(), like this:
try{
var gamer = '';//variable to capture json data
var options = {
uri: 'http://localhost:4000/gamers/'+gamer._id+'/find',
json: true
};
RequestPromise(options)
.then(function (data) {
gamer = data;//capturing response
// here is the rigth place perofrm operations on the answer, as this part of the code gets executed after promise reolves. BTW. Then you don't need variable gamer.
})
.catch(function (err) {
console.log("Error saving player data !");
});
// here is not the right place to do something with gamer as this is executed as soon as promise is initialized, not as it resolves. This means your variable will have initial value here
}catch(err){
res.status(500).send({
message: err.message || 'An error occurred generating player teams !'
});
}

DialogFlow V2 Webhook - Expects Speech responses Immediately and not after async requests

I have a DialogFlow V2 node.js webhook.
I have an intent that is called with a webhook action:
const { WebhookClient } = require('dialogflow-fulfillment');
const app = new WebhookClient({request: req, response: res});
function exampleIntent(app) {
app.add("Speak This Out on Google Home!"); // this speaks out fine. no error.
}
Now, if I have an async request which finishes successfully, and I do app.add in the success block like this:
function exampleIntent(app) {
myClient.someAsyncCall(function(result, err) {
app.add("This will not be spoken out"); // no dice :(
}
// app.add("but here it works... so it expects it immediately");
}
... then Dialog Flow does not wait for the speech to be returned. I get the error in the Response object:
"message": "Failed to parse Dialogflow response into AppResponse, exception thrown with message: Empty speech response",
How can I make DialogFlow V2 wait for the Webhook's Async operations to complete instead expecting a speech response immediately?
NOTE: This problem only started happening in V2. In V1, app.ask worked fine at the tail-end of async calls.
exampleIntent is being called by the main mapper of the application like this:
let actionMap = new Map();
actionMap.set("my V2 intent name", exampleIntent);
app.handleRequest(actionMap);
And my async request inside myClient.someAsyncCall is using Promises:
exports.someAsyncCall = function someAsyncCall(callback) {
var apigClient = getAWSClient(); // uses aws-api-gateway-client
apigClient.invokeApi(params, pathTemplate, method, additionalParams, body)
.then(function(result){
var result = result.data;
var message = result['message'];
console.log('SUCCESS: ' + message);
callback(message, null); // this succeeds and calls back fine.
}).catch( function(error){
console.log('ERROR: ' + error);
callback(error, null);
});
};
The reason it worked in V1 is that ask() would actually send the request.
With V2, you can call add() multiple times to send everything to the user in the same reply. So it needs to know when it should send the message. It does this as part of dealing with the response from your handler.
If your handler is synchronous, it sends the reply immediately.
If your handler is asynchronous, however, it assumes that you are returning a Promise and waits till that Promise resolves before sending the reply. So to deal with your async call, you need to return a Promise.
Since your call is using Promises already, then you're in very good shape! The important part is that you also return a Promise and work with it. So something like this might be your async call (which returns a Promise):
exports.someAsyncCall = function someAsyncCall() {
var apigClient = getAWSClient(); // uses aws-api-gateway-client
return apigClient.invokeApi(params, pathTemplate, method, additionalParams, body)
.then(function(result){
var result = result.data;
var message = result['message'];
console.log('SUCCESS: ' + message);
return Promise.resolve( message );
}).catch( function(error){
console.log('ERROR: ' + error);
return Promise.reject( error );
});
};
and then your Intent handler would be something like
function exampleIntent(app) {
return myClient.someAsyncCall()
.then( function( message ){
app.add("You should hear this message": message);
return Promise.resolve();
})
.catch( function( err ){
app.add("Uh oh, something happened.");
return Promise.resolve(); // Don't reject again, or it might not send the reply
})
}

Bad Request error in uber api

I've been trying to get the uber price estimates endpoint working, but I'm stuck on an error that leads me to a blank page saying, "Bad Request." The console also says "callback not a function" but I can't seem to find out what is wrong.
My route:
// Get an upfront fare before requesting a ride
app.get('/v1.2/estimates/price', function(request, response) {
// extract the query from the request URL
var query = request.query;
// if no query params sent, respond with Bad Request
if (!query || !query.lat || !query.lng) {
response.sendStatus(400);
} else {
uber.estimates.getPriceForRouteAsync( {
"product_id": "33de8094-3dc4-4ca9-8f67-243275f57623",
"start_latitude": "38.9597897",
"start_longitude": "-94.60699369999999",
"end_latitude": "39.010969",
"end_longitude": "-94.61509899999999"
})
.then(function(res) {
log(res);
})
.error(function(err) {
console.error(err);
});
}
});
Any help is appreciated.
Please check out the README for node-uber. The method does not take a JSON object but the arguments in the method call:
uber.estimates.getPriceForRouteAsync(38.9597897, -94.606994, 39.010969, -94.615098)
.then(function(res) { console.log(res); })
.error(function(err) { console.error(err); });
Also, the product ID is not needed as the /estimates/price endpoint returns an array of estimates for each product.

Resources