ReferenceError: conv is not defined in actions-on-google - webhooks

I want to implement Suggestions chips in my Dialog flow (to be use in Google Assistant)..But I am getting this error
"ReferenceError: conv is not defined"
which i didn't understand.I have go through the official docs but what am i missing? I have also added actions_intent_OPTION in his Event
following is my code
const functions = require('firebase-functions');
const {actionssdk} = require('actions-on-google');
const app = actionssdk({debug: true});
var admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
var firestore = admin.firestore();
exports.webhook = functions.https.onRequest((request, response) => {
switch (request.body.result.action) {
case 'countitem':
firestore.collection('orders').get()
.then((querySnapshot) => {
var orders = [];
querySnapshot.forEach((doc) => { orders.push(doc.data()) });
// now orders have something like this [ {...}, {...}, {...} ]
response.send({
speech: `you have ${orders.length} orders11, would you like to see them? (yes/no)`
});
})
.catch((err) => {
console.log('Error getting documents', err);
response.send({
speech: "something went wrong when reading from database"
})
})
conv.ask(new Suggestions('Suggestion Chips'));
conv.ask(new Suggestions(['suggestion 1', 'suggestion 2']));
break;
default:
response.send({
speech: "no action matched in webhook"
})
}
});

The issue is that conv isn't defined. Typically, if you're using the actions-on-google library, conv is passed to your fulfillment function and contains methods you can use to set replies and so forth.
It looks like you're handling everything yourself and generating the JSON response manually. If so, you should consult the guide for using JSON as part of your webhook and the repository of JSON examples.

Related

Can set data in Redis server but can't get it

I can set the value for a key in my Redis server (when I flushall, run this code, and then get key in redis-cli, I get back the proper values), but when I try to get key values through my NodeJs server, it never even logs out 'got data'.
I considered maybe this was because these functions were running asynchronously, and I was asking to get values that weren't yet stored in the cache, but that does not explain why it wouldn't print 'got data' ever.
My console logs-> 'start'->'data saved'->'end' (and no 'got data', ever)
In redis-cli-> flushall->get test->(nil)->run app.js(in the other terminal)->get test->"1, 2, 3, 4, 5"
I get no errors at all, the code runs, but does not do what I want it to.
Also, don't know if this is relevant, but when connecting to the Redis server, just Redis.createClient() only created a client but did not connect and when I looked it up, the general idea I got was that newer versions did not connect automatically and you had to manually redisClient.connect().
I struggled with this a bit at the start but seemed to have sorted this problem, but just thought I'd mention it, if I messed up somehow, please correct me, as I'm pretty new to NodeJs and codng in general.
My code:
const redisClient = Redis.createClient();
redisClient.connect();
const data = [1, 2, 3, 4, 5];
app.get('/', async(req, res, next) => {
console.log('start')
await redisClient.set('test', data);
console.log('data saved');
await redisClient.get('test', (error, test)=>{
console.log('got data');
console.log(test);
});
console.log('end');
});
Thanks!
I have seen your code. Based on my investigation you should remove the callback and keep await only while you get the data from redis.
I have investigated more this issue and have found that client.get() and client.set() function runs asynchronously. Hence it would achieve this way.
client.set('foo', 'bar', (err, reply) => {
if (err) throw err;
console.log(reply);
client.get('foo', (err, reply) => {
if (err) throw err;
console.log(reply);
});
});
But everytime is not the same use-case that we should set and get the value immediately.
To get rid of this, Following are the options.
Promises and async/await
you can promisify a subset of node_redis functions one at a time using native Node.js promises and util.promisify:
example:
const redis = require('redis');
const { promisify } = require('util');
const runApplication = async () => {
const client = redis.createClient();
const setAsync = promisify(client.set).bind(client);
const getAsync = promisify(client.get).bind(client);
await setAsync('foo', 'bar');
const fooValue = await getAsync('foo');
console.log(fooValue);
};
I have used the await here and solve an issue. In addition to that you can use redis.get().then() also to fetch the data rather than a callback.
I am also attaching the link with an example provided by redis repo
https://github.com/redis/node-redis/blob/master/examples/connect-as-acl-user.js
Following is the code, I have tested and it is working fine now.
redis.js
const redis = require("redis");
const redisClient = redis.createClient({
url: "redis://host:6379",
password: "password",
});
redisClient.connect();
// const { promisify } = require("util");
// promisify(redisClient.get).bind(redisClient);
// promisify(redisClient.set).bind(redisClient);
module.exports = redisClient;
index.js
const express = require("express");
const app = express();
const redisClient = require("./redis");
app.get("/set", async (req, res, next) => {
try {
const data = req.query.p;
await redisClient.set("test", data);
res.status(200).json({
message: "data cached",
data: data,
});
} catch (err) {
console.error(err);
res.status(500).json({
message: "Something went wrong",
});
}
});
app.get("/get", async (req, res, next) => {
try {
// const data = await redisClient.get("test");
const data = await reddisClient.get("test").then((data) => {
return data;
});
res.status(200).json({
message: "Cached data retrieved",
data,
});
} catch (err) {
console.error(err);
res.status(500).json({
message: "Something went wrong",
});
}
});
app.listen(process.env.PORT || 3000, () => {
console.log("Node server started");
});
Please find attached a screenshot of the output.
So the final thought is that, when we are using callback and wants to execute the code synchronously you should either use callback inside callback (but it is created callback hell, so it would not suggested anymore) or you should use promise/async await/native promisify library of nodejs.
Please visit below link to get the simplest understanding and example.
https://docs.redis.com/latest/rs/references/client_references/client_nodejs/
Hope my question clear your mind. I am happy to accept the relevant suggestion to improve an answer.

Unhandled Rejection (FirebaseError): No document to update

I'm still very new to coding so bear with me! I have followed a youtube course to build a note app and get a base to work from, but I'm now getting this error at random times when deleting the notes in firebase, hoping someone might be able to spot what's cooking here!
"Unhandled Rejection (FirebaseError): No document to update: projects/speakle-dc94b/databases/(default)/documents/notes/GdWPrQNxR3Z9TFMWmqOZ"
And it's referencing the node modules like so:
screenshot of the error in chrome
The code I have that interacts with firebase looks like this:
componentDidMount = () => {
firebase
.firestore()
.collection('notes')
.onSnapshot(serverUpdate => {
const notes = serverUpdate.docs.map(_doc => {
const data = _doc.data();
data['id'] = _doc.id;
return data;
});
console.log(notes);
this.setState({ notes: notes });
});
}
selectNote = (note, index) => this.setState({ selectedNoteIndex: index, selectedNote: note });
noteUpdate = (id, noteObj) => {
firebase
.firestore()
.collection('notes')
.doc(id)
.update({
title: noteObj.title,
body: noteObj.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
}
newNote = async (title) => {
const note = {
title: title,
body: ''
};
const newFromDB = await firebase
.firestore()
.collection('notes')
.add({
title: note.title,
body: note.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
const newID = newFromDB.id;
await this.setState({ notes: [...this.state.notes, note] });
const newNoteIndex = this.state.notes.indexOf(this.state.notes.filter(_note => _note.id === newID)[0]);
this.setState({ selectedNote: this.state.notes[newNoteIndex], selectedNoteIndex: newNoteIndex });
}
deleteNote = async (note) => {
const noteIndex = this.state.notes.indexOf(note);
await this.setState({ notes: this.state.notes.filter(_note => _note !== note) })
if(this.state.selectedNoteIndex === noteIndex) {
this.setState({ selectedNoteIndex: null, selectedNote: null});
} else {
this.state.notes.lenght > 1 ?
this.selectNote(this.state.notes[this.state.selectedNoteIndex - 1], this.state.selectedNoteIndex - 1) :
this.setState({ selectedNoteIndex: null, selectedNote: null });
}
firebase
.firestore()
.collection('notes')
.doc(note.id)
.delete()
.then(function() {
console.log("Document successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
}
}
It simply means that there is no document of that name to be uploaded.
you could either use set() or add() to add the document because it is not present.
noteUpdate = (id, noteObj) => {
firebase
.firestore()
.collection('notes')
.doc(id)
.update({
title: noteObj.title,
body: noteObj.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
}
replace the above code with this
noteUpdate = (id, noteObj) => {
firebase
.firestore()
.collection('notes')
.doc(id)
.add({
title: noteObj.title,
body: noteObj.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
}
or
noteUpdate = (id, noteObj) => {
firebase
.firestore()
.collection('notes')
.doc(id)
.set({
title: noteObj.title,
body: noteObj.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
}
I was working with something like this only especially in Cloud Functions and while writing an endpoint for doing some operation I came across the below-quoted error.
I was trying to read a document in a collection and if it existed I was trying to write a new doc into another collection. So it was kind of a nested code.
A piece of my code.
const firstDocRef = db.collection('myFirstCollection').doc('myDocName');
const existDoc = firstDocRef.get()
.then((resDoc)=>{
if(resDoc.exists)
{
db.collection('mySecondCollection').add({
.
.
.
.
.
orderCreatedAt:Firestore.FieldValue.serverTimestamp()
})
.then((new_doc)=>{
return res.status(200);
// return 200 ok what we were trying to achieve has completed.
})
.catch(()=>{
console.log("Log, as write failed somehow");
return res.status(500);
// return a 500 internal server error occurred
});
}
else
{
console.log("My first condition wasn't met, so logged it");
return res.end();
// properly terminate the processing of request
}
});
/*.catch((err)=>{
console.log("Our first doc presence check couldn't complete and hence I arrived here, log it");
res.writeHead(500);
return res.end();
// again give back 500 to client
});*/
UnhandledPromiseRejectionWarning: ReferenceError: Firestore is not defined
UnhandledPromiseRejectionWarning: Unhandled promise rejection.
This error originated either by throwing inside of an async function without a catch block
Now I am also new to Firebase but I came across this and somehow solved it.
So I was not getting the above error if I was putting in a catch block in get() document.
Strange huh!
Removed the catch block by commenting it. Got this error.
Now, this is a haywire error, it says the catch is not there, but we did it on purpose.
So I began searching, came across this question here on stack overflow and saw it's still unanswered. Searched and read the documentation myself.
I would like to tell you that this isn't because of any Firestore Security Rules, or anything else. Because I came across some guesses around these notions too while searching for an answer.
The common thing we all are doing here is that we are trying to achieve the ServerTimeStamp at FireStore
I would like to bring your notice to my imports in my node cloud function code.
const functions = require('firebase-functions');
const express = require('express');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
So you see, I am using the new way of getting the permission to use Firestore because I am trying to establish a cloud function.
Now this is the documentation reference provided by Google: Click here!
The right syntax proposed by above API reference is
Firestore.FieldValue.serverTimestamp()
It is the culprit, it isn't providing me any timestamp and if there is not a catch block unhandled promise error is occuring and no error is being shown while debugging, it just doesn't work.
I did this, Solution part:
Even after those imports in my Node Program, I imported this:
const {Firestore} = require('#google-cloud/firestore');
Now all I did was that I used the statement in the timestamp field as
Firestore.FieldValue.serverTimestamp()
Exactly as mentioned and even used a catch block just in case any other problem occurs while at production. That is using db constant to do all the Database Transactional Stuff and just for serverTimeStamp I have to bring in new import.
And it worked, I guess require('#google-cloud/firestore') statement imported as {FireStore} brings in all the things that are required for the FieldValue thing to use as a reference.
I hope it helps any new person looking for it and saves a lot of time which I wasted searching for a solution.
I have verified it by running the cloud function on firebase emulator.
You could simply do it like this
You
Get it
If it exsits: you update it
If it doesn't exist, you set it.
const docRef = this.db.collection("users_holdings").doc(userId);
docRef.get().subscribe((doc) => {
if (doc.exists) {
docRef.update({
stockHoldings: stockHoldings,
});
} else {
docRef.set({
stockHoldings: stockHoldings,
});
}
});

Firebase Cloud Functions get updated document Details

I'm trying to write a cloud function where if my app changed some string in user's firestore DB. A cloud function need to send a push notification. Database architecture is Messages => {UID} => UpdatedMessages . The problem is I cannot figure How to retrive which updateMessage under which UID has been updated.
const functions = require('firebase-functions');
const admin = require('firebase-admin')
admin.initializeApp()
const toUpperCase = (string)=> string.toUpperCase()
var registrationToken = 'dfJY6hYzJyE:APdfsdfsdddfdfGt9HMfTXmei4QFtO0u1ePVpNYaOqZ1rnDpB8xfSjx7-G6tFY-vWQY3vDPEwn_iZVK2PrsGUVB0q9L_QoRYpLJ3_6l1SVHd_0gQxJb_Kq-IBlavyJCkgkIZ';
exports.sendNotification = functions.firestore
.document('messages/{userId}/{updatedMessage}')
.onUpdate((change, context) => {
var message = {
data: {
title: 'Update',
body: 'New Update'
},
token: registrationToken
};
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent messagesssss:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
});
Simply I need to rerive "var registrationToken" from UID .
You have to use the params property of the context object as follows
exports.sendNotification = functions.firestore
.document('messages/{userId}/{updatedMessage}')
.onUpdate((change, context) => {
const userId = context.params.userId;
const updatedMessage = context.params.updatedMessage;
var message = {
data: {
title: 'Update',
body: updatedMessage //For example, use the value of updatedMessage here
},
//...
};
//IMPORTANT: don't forget to return the promise returned by the asynchronous send() method
return admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent messagesssss:', response);
return null;
})
.catch((error) => {
console.log('Error sending message:', error);
return null;
});
});
See https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters and https://firebase.google.com/docs/reference/functions/functions.EventContext#params for more info.
About the remark in the above code noted as "IMPORTANT", you may watch the official Firebase video series here: https://firebase.google.com/docs/functions/video-series/. In particular watch the three videos titled "Learn JavaScript Promises" (Parts 2 & 3 especially focus on background triggered Cloud Functions, but it really worth watching Part 1 before).

Dialogflow V2 error - Async call is not working - Promise Error? firebase function

I wanted to get some data from mongodb (using mongoose framework) but unable to get the data,
previously i was using callback method as described here to getting data which was working well for me in "action on google v1" but not working in v2,
then i read here that we must need to use promise in order to make async call in "action on google v2", then i refactored my code according to the instruction of Mr. #prisoner in the question above
you can see my code here:
import * as functions from 'firebase-functions';
import {
dialogflow,
SimpleResponse,
Suggestions,
DialogflowConversation,
DialogflowApp
} from 'actions-on-google'
import { Model } from './db'
const app = dialogflow({ debug: false })
app.middleware((conv) => {
conv["hasScreen"] =
conv.surface.capabilities.has('actions.capability.SCREEN_OUTPUT');
conv["hasAudioPlayback"] =
conv.surface.capabilities.has('actions.capability.AUDIO_OUTPUT');
});
app.intent('Get Some Data', (conv) => {
console.log("Get Some Data Intent triggered")
return new Promise(function (resolve, reject) {
Model.find({}, (err, result: any) => {
if (!err) {
if (!result.length) {
console.log("no data found")
conv.ask(new SimpleResponse({
speech: "no data found"
}))
resolve();
} else {
console.log("lots of data found")
conv.ask(new SimpleResponse({
speech: "lots of data found"
}));
resolve();
}
} else {
console.log("Error in getting data: ", err);
conv.ask(new SimpleResponse({
speech: "Error in getting data"
}))
resolve();
}
})
})
});
exports.webhook = functions.https.onRequest(app);
it is still not working for me, and function is ending with timeout
Actually i have my app up and running properly in v1, and now I'm trying to migrate from v1 to v2 since i need to use some latest features which are not available in v1 like voice authentication and other new features.
any help will be warmly welcomed
The page you referenced at the Mongoose documentation that talks about find() also gives information about how to use it with Promises. The find() call returns a Query and you can use query.exec() to get the Promise that you'll then work with.
So the code might look something like this (untested, since I don't use Mongoose):
app.intent('Get Some Data', (conv) => {
var query = Model.find({});
return query.exec()
.then( result => {
if( !result || !result.length ){
return conv.ask(new SimpleResponse({
speech: "no data found"
}));
} else {
return conv.ask(new SimpleResponse({
speech: "some data found"
}));
}
})
.catch( err => {
return conv.close(new SimpleResponse({
speech: "something went wrong"
}));
});
});

Unhandled Rejection: Headers cant be set after they are sent

I am creating a chatbot in Dialogflow. I am trying to add the data to the database when its throwing an error of Unhandled Rejection.
This is my index.js file.
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
//const {Card, Suggestion} = require('dialogflow-fulfillment');
var admin = require('firebase-admin');
//require("firebase/firestore");
admin.initializeApp(functions.config().firebase);
var firestore = admin.firestore();
//var db = firebase.firestore();
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
var addRef = firestore.collection('Admission');
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
console.log("request.body.queryResult.parameters: ", request.body.queryResult.parameters);
let params = request.body.queryResult.parameters;
/* 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?`);
} */
let responseJson ={};
function yourFunctionHandler(agent) {
var docRef = firestore.collection('users');
name = request.body.queryResult.parameters['myname'];
coll = request.body.queryResult.parameters['college_name'];
name = name.charAt(0).toUpperCase() + name.slice(1);
let balanceresponse = {};
console.log(name);
return docRef.add({
myname: name,
college: coll
})
.then((querySnapshot)=>{
balanceresponse = {
"fulfillmentText": 'Sure '+name+', Do you want to know about Admissions, Fees, Graduates and PG, Contact information, Media or Testimonials?'
}
console.log('before response.send()');
response.send(balanceresponse);
/* console.log('before response.send()');
response.send({
fulfillmentText:
'Sure '+name+', Do you want to know about Admissions, Fees, Graduates and PG, Contact information, Media or Testimonials?'
//response.json(responseJson);
}); */
console.log('after response.send()');
return 1;
})
.catch(error => {
response.send({
fulfillmentText:
'Something went wrong with the database'
});
});
}
function AdmissionHandler(agent) {
console.log("inside Admission Handler");
let balanceresponse = {};
let Question = request.body.queryResult.parameters['question'];
addRef.where("Question", "==", Question)
.get().then((querySnapshot)=>{
if (querySnapshot) {
console.log("Document data:");
const tips = querySnapshot.docs;
const tipIndex = Math.floor(Math.random() * tips.length);
const tip = tips[0];
balanceresponse = {
"fulfillmentText": ' Anything else you wanna know?'
}
console.log('before response.send()');
response.send(balanceresponse);
/* response.send({
fulfillmentText:
//firestore.collection(addRef.Answer)+
' Anything else you wanna know?'
}); */
return 1;
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
return 1;
})
.catch(function(error) {
console.log("Error getting document:", error);
});
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
// intentMap.set('Default Welcome Intent', welcome);
// intentMap.set('Default Fallback Intent', fallback);
intentMap.set('GetName', yourFunctionHandler);
intentMap.set('AdmissionCustom', AdmissionHandler);
agent.handleRequest(intentMap);
});
This is the error I receive:
I have seen few similar questions here but none of them are answered. Can anyone please help? I have been stuck in this for over a week already.
The problem is that the yourFunctionHandler(agent) function is doing things asynchronously, but isn't returning a Promise. Instead, it returns nothing, so the processing returns immediately without having a message sent.
Since it looks like myDoc.add() returns a Promise, this is easy to handle by making that return myDoc.add(...).then(...) and so forth. It might look something like this:
function yourFunctionHandler(agent) {
return docRef.add({
myname: name,
college: coll
})
.then(()=>{
response.send({
fulfillmentText:
'Sure '+name+', Do you want to know about Admissions, Fees, Graduates and PG, Contact information, Media or Testimonials?'
});
return 1;
})
.catch(error => {
//console.log('érror',e);
response.send({
fulfillmentText:
'Something went wrong with the database'
});
});
}
Additionally, you're mixing handling the response yourself (by calling response.send()) and using the Dialogflow agent.handleRequest(), which will create a response for you.
You should either use the Dialogflow methods to generate the reply with something like
agent.add("No such document found.");
or use the values in the JSON yourself to determine which handler to call with something like
const intentName = request.body.queryResult.intent.name;
const handler = intentMap[intentName];
handler();
(You may need to vary this. It looks like from your code you're using Dialogflow v1, which I've reflected, and the path for the intent name changes for v2. You also should check for the handler not existing, may want to send parameters, etc.)

Resources