Reputation: 69
So I am an extremely beginner programmer trying to understand how to call/get data from Google Distance Matrix API in purely Javascript.
Below is my codes
https = require('https')
function getDistance(app){
console.log('testing1');
let defaultPath = 'maps.googleapis.com/maps/api/distancematrix/json?units=metric';
let APIKey = '&key=<API KEY HERE>';
let origin = 'Tampines Avenue 1, Temasek Polytechnic';
let destination = 'Tampines Central 5, Tampines Mall';
let newPath = defaultPath+ '&origins=' + origin + '&destinations=' +destination + APIKey;
console.log('https://'+ newPath); //this prints the correct path
https.get('https://'+ newPath, (res) =>{ //i assume the problem begins here?
console.log('testing2')//doesnt print
let body = ''; //no idea what this does. Copied from a school lab sheet
res.on('data', (d) => {
console.log('testing3') //this doesn't print
let response = JSON.parse(body);
let distance = response.rows[0].elements.distance.text //are these correct?
let travelTime = response.rows[0].elements.duration.text
console.log(distance) //doesnt print
console.log(response.rows[0]) //doesnt print
app.add('distance between is ' + distance + '. Time taken is ' + travelTime);
console.log(response);
});
});
}
I'm pretty sure the 'https://'+ newPath is correct as it is printed in the console.log
And throwing the link into a browser I get this as result
so can someone please explain to me what i'm doing wrong? Oh and also, dont know if this is necessary but im doing this in dialogflow.cloud.google.com as a chatbot for my assignment
This is the error I get
Error: No responses defined for platform: undefined at WebhookClient.send_ (/srv/node_modules/dialogflow-fulfillment/src/dialogflow-fulfillment.js:428:13) at promise.then (/srv/node_modules/dialogflow-fulfillment/src/dialogflow-fulfillment.js:246:38) at at process._tickDomainCallback (internal/process/next_tick.js:229:7)
Upvotes: 1
Views: 659
Reputation: 50741
You don't show all your code, but it looks like getDistance()
is your Intent Handler function that you've registered in code that you're not showing.
If so, and if you're making an asynchronous call to an API, you need to return a Promise to indicate that you're waiting for the HTTP call to complete before you send the result.
Without the Promise, the function completes right after it makes the call with http.get()
, but without anything being set for the response with app.add()
.
While it is possible to turn an event-based call (what you're doing now) into a Promise, it isn't that easy if you're not familiar with it, and there are better solutions.
Using a package such as request-promise (and more likely request-promise-native - it uses the same syntax, but request-promise has the documentation) is far easier. With this, you would return the Promise that is generated by the http call, and in the then()
clause of it, you would make your calls to app.add()
.
I haven't tested it, but it might look something like this:
const rp = require('request-promise-native');
function getDistance(app){
console.log('testing1');
let defaultPath = 'maps.googleapis.com/maps/api/distancematrix/json?units=metric';
let APIKey = '&key=<API KEY HERE>';
let origin = 'Tampines Avenue 1, Temasek Polytechnic';
let destination = 'Tampines Central 5, Tampines Mall';
let newPath = defaultPath+ '&origins=' + origin + '&destinations=' +destination + APIKey;
let url = 'https://'+newPath;
console.log(url);
rp.get(url)
.then( response => {
console.log(response);
let distance = response.rows[0].elements.distance.text
let travelTime = response.rows[0].elements.duration.text
app.add('distance between is ' + distance + '. Time taken is ' + travelTime);
})
.catch( err => {
console.log( err );
app.add('Something went wrong.');
});
};
Upvotes: 0
Reputation: 77063
I found a similar problem on GitHub: https://github.com/dialogflow/dialogflow-fulfillment-nodejs/issues/22
The solution was
Okay, so here's what I did to make this work properly.
I used request-promise-native instead of http to make the AJAX Call.
const rp = require('request-promise-native');
I returned a promise from the handler of the promise that rp returns.
return rp(options).then(data => { // Extract relevant details from data. // Add it to the agent. agent.add('Here's the data: ', JSON.stringify(data)); return Promise.resolve(agent); });
The full code is
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const rp = require('request-promise-native');
const { WebhookClient } = require('dialogflow-fulfillment');
const { Card, Suggestion } = require('dialogflow-fulfillment');
const { Carousel } = require('actions-on-google');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
const imageUrl = 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png';
const imageUrl2 = 'https://lh3.googleusercontent.com/Nu3a6F80WfixUqf_ec_vgXy_c0-0r4VLJRXjVFF_X_CIilEu8B9fT35qyTEj_PEsKw';
const linkUrl = 'https://assistant.google.com/';
const API_KEY = 'YOUR-API-KEY-HERE';
const server = express();
server.use(
bodyParser.urlencoded({
extended: true
})
);
server.use(bodyParser.json());
server.post('/dialog-flow-fulfillment', (request, response) => {
const agent = new WebhookClient({ request, response });
function googleAssistantOther(agent) {
let conv = agent.conv();
conv.ask(`Sure! Details about ${agent.parameters.movie} coming your way!`);
return getMovieDataFromOMDb(agent.parameters.movie).then(movie => {
conv.ask(`Okay! So there you go.`);
conv.ask(new Card({
title: `${movie.Title}(${movie.Year})`,
imageUrl: movie.Poster,
text: `${movie.Rated} | ${movie.Runtime} | ${movie.Genre} | ${movie.Released} (${movie.Country})`,
buttonText: 'Website',
buttonUrl: movie.Website || `https://www.imdb.com/title/${movie.imdbID}`
}));
conv.ask(new Suggestion(`More details`));
conv.ask(new Suggestion(`Another movie`));
agent.add(conv);
return Promise.resolve(agent);
});
}
function welcome(agent) {
agent.add(`Welcome to my agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function getMovieDetailsOther(agent) {
return getMovieDataFromOMDb(agent.parameters.movie).then(movie => {
// const responseDataToSend = `${movie.Title} is a ${
// movie.Actors
// } starer ${movie.Genre} movie, released in ${
// movie.Year
// }. It was directed by ${movie.Director}`;
// console.log(`Generated response as ${responseDataToSend}`);
// agent.add(responseDataToSend);
agent.add(`Okay! So there you go.`);
agent.add(new Card({
title: `${movie.Title}(${movie.Year})`,
imageUrl: movie.Poster,
text: `${movie.Rated} | ${movie.Runtime} | ${movie.Genre} | ${movie.Released} (${movie.Country})`,
buttonText: 'Website',
buttonUrl: movie.Website || `https://www.imdb.com/title/${movie.imdbID}`
}));
agent.add(new Suggestion(`More details`));
agent.add(new Suggestion(`Another movie`));
return Promise.resolve(agent);
}, error => {
console.log(`Got an error as ${error}`);
agent.add(`Sorry bout that! An error prevented getting data for: ${agent.parameters.movie || 'the requested movie'}`
);
})
.catch(function (err) {
console.log(`Caught an err as ${err}`);
agent.add(err);
});
// agent.add(`This message is from Dialogflow's Cloud Functions for Firebase editor!`);
// const newCard = new Card({
// title: `Title: this is a card title`,
// imageUrl: imageUrl,
// text: `This is the body text of a card. You can even use line\n breaks and emoji! 💁`,
// buttonText: 'This is a button',
// buttonUrl: linkUrl
// });
// // newCard.setPlatform('facebook');
// agent.add(newCard);
// agent.add(new Suggestion(`Quick Reply`));
// agent.add(new Suggestion(`Suggestion`));
// agent.setContext({ name: 'weather', lifespan: 2, parameters: { city: 'Rome' }});
}
function moreDetailsOther(agent) {
return getMovieDataFromOMDb(agent.parameters.movie).then(movie => {
agent.add(`Okay! I've got you covered on this too.`);
agent.add(`So the ${movie.Actors} starer ${movie.Type} is produced by ${movie.Production}, is directed by ${movie.Director}`);
agent.add(`It ${movie.Awards}. It's available in ${movie.Language}`);
agent.add(`Written by ${movie.Writer}, it plots ${movie.Plot}`);
agent.add(new Suggestion(`Stats on the movie`));
agent.add(new Suggestion(`Another movie`));
return Promise.resolve(agent);
}, error => {
console.log(`Got an error as ${error}`);
agent.add(`Sorry bout that! An error prevented getting data for: ${agent.parameters.movie || 'the requested movie'}`
);
})
.catch(function (err) {
console.log(`Caught an err as ${err}`);
agent.add(err);
});
}
function movieStatsOther(agent) {
return getMovieDataFromOMDb(agent.parameters.movie).then(movie => {
let ratingDetails = `${movie.Title} scored `;
movie.Ratings.forEach(rating => {
ratingDetails += `${rating.Value} on ${rating.Source} `
});
agent.add(`Sure! Here are the stats.`);
agent.add(ratingDetails);
agent.add(new Suggestion(`Another movie`));
return Promise.resolve(agent);
}, error => {
console.log(`Got an error as ${error}`);
agent.add(`Sorry bout that! An error prevented getting data for: ${agent.parameters.movie || 'the requested movie'}`
);
})
.catch(function (err) {
console.log(`Caught an err as ${err}`);
agent.add(err);
});
}
function getMovieDataFromOMDb(movieName) {
const movieToSearch = movieName || 'The Godfather';
const options = {
uri: 'https://www.omdbapi.com/',
json: true,
qs: {
t: movieToSearch,
apikey: API_KEY
}
};
return rp(options);
}
// Run the proper handler based on the matched Dialogflow intent
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
if (agent.requestSource === agent.ACTIONS_ON_GOOGLE) {
intentMap.set(null, googleAssistantOther);
// intentMap.set('More Details', googleAssistantMoreDetails);
// intentMap.set('Movie Stats', googleAssistantMovieStats);
} else {
intentMap.set('Get Movie Details', getMovieDetailsOther);
intentMap.set('More Details', moreDetailsOther);
intentMap.set('Movie Stats', movieStatsOther);
}
agent.handleRequest(intentMap);
});
server.listen(process.env.PORT || 8000, () => {
console.log('Server is up and running...');
});
Codepen: https://codepen.io/siddajmera/pen/eraNLW?editors=0010
Upvotes: 1