Dialogflow Google Calendar integration crashing - javascript

I try to get events from a Google Calendar and pass them to my chatbot in DialogFlow.
'use strict';
const functions = require('firebase-functions');
const {google} = require('googleapis');
const {WebhookClient} = require('dialogflow-fulfillment');
const {moment} = require('moment');
const calendarId = "some_id";
const serviceAccount = {some_account};
const serviceAccountAuth = new google.auth.JWT({
email: serviceAccount.client_email,
key: serviceAccount.private_key,
scopes: 'https://www.googleapis.com/auth/calendar'
});
const calendar = google.calendar('v3');
process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log("Parameters", agent.parameters);
function checkAppointment (agent) {
return check().then((value) => {
agent.add(`yep ${value.data}`);
}).catch(() => {
agent.add(`I'm sorry.`);
});
}
let intentMap = new Map();
intentMap.set('CheckAppointment', checkAppointment);
agent.handleRequest(intentMap);
});
function check(agent){
var beginning = moment().format("YYYY-MM-DDT00:01:00Z");
var end = moment().format("YYYY-MM-DDT23:59:00Z");
return new Promise((resolve, reject) => {
calendar.events.list({
auth: serviceAccountAuth,
calendarId: calendarId,
timeMin: beginning,
timeMax: end,
}, (err, calendarResponse) => {
console.log(calendarResponse.data);
if (err || calendarResponse.data.items.length <= 0) {
reject(err || new Error('theres nothing here'));
} else {
resolve(calendarResponse);
}
});
});
}
Here's the log after trigerring the intent:
It just says "crash" and therefore I have no idea what the bug could possibly be. Any ideas what to do to find out?

Related

Not able to get Access Token from Google Api

I am new to GTM+GA.I am trying to display google Analytics(GA4) reports on my webpage. I created Oauth Client Id in google cloud console and also done other settings in Google cloud console. Through javascript code i am trying to get access token from google Api and I am getting below exception.
After successful authentication I will integrate GA repots with my web page. Below is my javascript code for getting access token.
function main(propertyId = 'YOUR-GA4-PROPERTY-ID') {
propertyId = '347415282';
const {
OAuth2Client
} = require('google-auth-library');
const {
grpc
} = require('google-gax');
const http = require('http');
const url = require('url');
const open = require('open');
const destroyer = require('server-destroy');
const keys = require('./oauth2.keys.json');
const SCOPES = ['https://www.googleapis.com/auth/analytics.readonly'];
function getAnalyticsDataClient(authClient) {
const sslCreds = grpc.credentials.createSsl();
const credentials = grpc.credentials.combineChannelCredentials(
sslCreds,
grpc.credentials.createFromGoogleCredential(authClient));
return new BetaAnalyticsDataClient({
sslCreds: credentials,
});
}
function getOAuth2Client() {
return new Promise((resolve, reject) => {
const oAuth2Client = new OAuth2Client(
keys.web.client_id,
keys.web.client_secret,
'http://localhost:3000/oauth2callback');
const authorizeUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES.join(' '),
});
const server = http
.createServer(async(req, res) => {
try {
if (req.url.indexOf('/oauth2callback') > -1) {
const qs = new url.URL(req.url, 'http://localhost:3000')
.searchParams;
const code = qs.get('code');
console.log(`Code is ${code}`);
res.end(
'Authentication successful! Please return to the console.');
server.destroy();
const r = await oAuth2Client.getToken(code);
oAuth2Client.setCredentials(r.tokens);
console.info('Tokens acquired.');
resolve(oAuth2Client);
}
} catch (e) {
reject(e);
}
})
.listen(3000, () => {
console.info(`Opening the browser with URL: ${authorizeUrl}`);
open(authorizeUrl, {
wait: false
}).then(cp => cp.unref());
});
destroyer(server);
});
}
async function runReport() {
const oAuth2Client = await getOAuth2Client();
const analyticsDataClient = getAnalyticsDataClient(oAuth2Client);
const[response] = await analyticsDataClient.runReport({
property: `properties/${propertyId}`,
dateRanges: [{
startDate: '2020-03-31',
endDate: 'today',
},
],
dimensions: [{
name: 'city',
},
],
metrics: [{
name: 'activeUsers',
},
],
});
console.log('Report result:');
response.rows.forEach(row => {
console.log(row.dimensionValues[0], row.metricValues[0]);
});
}
runReport();
process.on('unhandledRejection', err => {
console.error(err.message);
process.exitCode = 1;
});
main(...process.argv.slice(2));
Please let me know how to get rid off this issue.
Regards,
Prabhash

One index.js file in fulfillment dialogflow with multiple functions/trigger to firestore #AskFirebase

I have the code below in the dialogflow ("firebase-functions": "^ 3.7.0"
and node: 10)
DialogFlow index.js with issue
Photos project in DialogFlow
I have four intents: Ligar, Desligar, Abrir e Fechar (all with fulfillment enabled)
I not have issue in deploy or in execution(logs cloud functions), but the only function that works is getLigar(). How to solve this?
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({request, response});
function getLigar(agent) {
var gpioalarmstateb = agent.parameters;
return db.collection('xxxxx').doc('yyyyy').get()
.then(doc => {
const xalarmstate = doc.data().gpioalarmstate;
if (!xalarmstate) {
db.collection('xxxxx').doc('yyyyy').update({
gpioalarmstate: true
})
}
agent.add(`Gpioalarmstate is ${gpioalarmstateb} xalarmstate is ${xalarmstate}.`);
});
}
function getDesligar(agent) {
var gpioalarmstateb = agent.parameters;
return db.collection('xxxxx').doc('yyyyy').get()
.then(doc => {
const xalarmstateD = doc.data().gpioalarmstate;
if (xalarmstateD) {
db.collection('xxxxx').doc('yyyyy').update({
gpioalarmstate: false
})
}
agent.add(`Gpioalarmstate is ${gpioalarmstateb} xalarmstate is ${xalarmstateD}.`);
});
}
function getAbrirr(agent) {
var gpiogaragestateb = agent.parameters;
return db.collection('xxxxx').doc('yyyyy').get()
.then(doc => {
const xgaragestate = doc.data().gpiogaragestate;
if (!xgaragestate) {
db.collection('xxxxx').doc('yyyyy').update({
gpiogaragestate: true
})
}
agent.add(`Gpiogaragestate is ${gpiogaragestateb} xgaragestate is ${xgaragestate}.`);
});
}
function getFechar(agent) {
var gpiogaragestateb = agent.parameters;
return db.collection('xxxxx').doc('yyyyy').get()
.then(doc => {
const xgaragestateF = doc.data().gpiogaragestate;
if (xgaragestateF) {
db.collection('xxxxx').doc('yyyyy').update({
gpiogaragestate: false
})
}
agent.add(`Gpiogaragestate is ${gpiogaragestateb} xgaragestate is ${xgaragestateF}.`);
});
}
let intentMap = new Map();
intentMap.set('Ligar', getLigar);
intentMap.set('Desligar', getDesligar);
intentMap.set('Abrir', getAbrirr);
intentMap.set('Fechar', getFechar);
agent.handleRequest(intentMap);
});
The answer was: I needed async before the 4 functions mentioned in the question and put await in the right place. Below I put an example that served for the first of the 4 functions:
async function getLigar(agent) {
return await db.collection('xxxxx').doc('yyyyy').get()

How to return REST API response after utility execution is finished in expressJs

I have written one POST endpoint in expressJS with node.when I a make call to API It runs a utility with setInterval() and I want to send the API response after utility executes clearInterval().
How I can I wait and send response after utility execution is finished?
Please see the code below
REST API code:
const router= express.Router();
const multer= require('multer');
const {readCSVFile}= require('../util/index');
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads');
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now()+'.xlsx');
}
});
var upload = multer({storage: storage});
router.post('/fileUpload', upload.single('filename'), async (req, res) => {
readCSVFile();
res.status(201).json({id:1});
});
router.get('/',(req,res)=>{
res.sendFile(__dirname+'/index.html');
});
module.exports=router;
Utilty Code
const config = require('config')
const excelToJson = require('convert-excel-to-json')
const HttpsProxyAgent = require('https-proxy-agent')
const AWS = require('aws-sdk')
const json2xls = require('json2xls')
const fs = require('fs')
const awsConfig = {
httpOptions: {
agent: new HttpsProxyAgent(
config.get('aws.proxy')
),
}
}
AWS.config.credentials = new AWS.SharedIniFileCredentials({
profile: config.get('aws.profile'),
})
AWS.config.update(awsConfig)
let uuidv4 = require('uuid/v4')
let csv = [];
const lexRunTime = new AWS.LexRuntime({
region: config.get('aws.region'),
})
let refreshId
const readCSVFile = () => {
const csvSheet = excelToJson({
sourceFile: './Test.xlsx',
})
csvSheet.Sheet1.forEach(element => {
csv.push((element.A.slice(0, element.A.length)))
})
runTask()
refreshId = setInterval(runTask, 1000)
}
let botParams = {
botAlias: config.get('bot.alias'),
botName: config.get('bot.name'),
sessionAttributes: {},
}
const missedUtterancesArray = []
const matchedUtterancesArray = []
let start = 0
let end = 50
let count = 50
const runTask = () => {
let itemsProcessed = 0
console.log('executing...')
const arrayChunks = csv.slice(start, end)
arrayChunks.forEach((element) => {
botParams.inputText = element
botParams.userId = `${uuidv4()}`
lexRunTime.postText(botParams, function (err, data) {
itemsProcessed++
if (err) console.log(err, err.stack)
else {
if (data.intentName === null) {
missedUtterancesArray.push({
Utterance: element,
})
}
else{
matchedUtterancesArray.push({
Utterance: element,
})
}
}
if (itemsProcessed === arrayChunks.length) {
start = csv.indexOf(csv[end])
end = start + count
}
if (start === -1) {
let xls = json2xls(missedUtterancesArray)
fs.writeFileSync('./MissedUtterances.xlsx', xls, 'binary')
let matchedXls = json2xls(matchedUtterancesArray)
fs.writeFileSync('./MatchedUtterances.xlsx', matchedXls, 'binary')
console.log('File saved successfully!! ')
console.log('Total Matched utterances count: ',csv.length-missedUtterancesArray.length)
console.log('Total Missed utterances count: ',missedUtterancesArray.length)
console.log('Total Utterances count: ',csv.length)
clearInterval(refreshId)
}
})
})
}
I would have needed few more information to answer this but pardon my try if this does not work -
the setInterval method in the readCSVFile the reason. Being an asynchronous function, this will not stop the code progression.
lexRunTime.postText also looks like asynchronous. I think you'd be better off with using promises while responding to the client.

On Deploying the Firebase Cloud Function getting an error of "Each then should return a value or throw"

I am new to Cloud Functions so I having issues with below code, the error is in the last part where the console.log is mentioned, please assist what shall I been done to deploy the Function successfully, as I am following a tutorial there is no such error for the name.
'use-strict'
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.sendNotifications = functions.firestore.document("users/{user_id}/notifications/{notification_id}").onWrite(event => {
const user_id = event.params.user_id;
const notification_id = event.params.notification_id;
return admin.firestore().collection("users").doc(user_id).collection("notifications").doc(notification_id).get().then(queryResult => {
const from_user_id = queryResult.data().from;
const message = queryResult.data().message;
const from_data = admin.firestore().collection("users").doc(from_user_id).get();
const to_data = admin.firestore().collection("user").doc(user_id).get();
return Promise.all([from_data, to_data]).then(result => {
const from_name = result[0].data().name;
const to_name = result[1].data().name;
const token_id = result[1].data().token_id;
const payload = {
notification: {
title: "Notification from:" + from_name,
body: message,
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(result =>{
console.log("notifcation sent");
});
});
});
});
By chaining your Promises and returning null in the last then(), as follows, you should solve your problem:
exports.sendNotifications = functions.firestore.document("users/{user_id}/notifications/{notification_id}").onWrite(event => {
const user_id = event.params.user_id;
const notification_id = event.params.notification_id;
return admin.firestore().collection("users").doc(user_id).collection("notifications").doc(notification_id).get()
.then(queryResult => {
const from_user_id = queryResult.data().from;
const message = queryResult.data().message;
const from_data = admin.firestore().collection("users").doc(from_user_id).get();
const to_data = admin.firestore().collection("user").doc(user_id).get();
return Promise.all([from_data, to_data]);
})
.then(result => {
const from_name = result[0].data().name;
const to_name = result[1].data().name;
const token_id = result[1].data().token_id;
const payload = {
notification: {
title: "Notification from:" + from_name,
body: message,
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload)
})
.then(messagingResponse => {
console.log("notification sent");
return null; //Note the return null here, watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/
});
});
You may have a look at the corresponding MDN documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining
Also, note that, in your code, it seems that you are not using the to_name constant.

Exporting data in a node file after it is returned from an API call to AWS Secrets Manager

Background
I am storing the database information for a RDS in AWS in the secrets manager. I am using the AWS-SDK to retrieve the password and other data so I can create a secrets object at run time. When I try and create this object and then export it, the object that is exported is always lacking the data that I expect to be returned from the aws-sdk.
What I Have Tried -
I have tried using async await but it is still exporting the object before all of the data is correctly populated.
Example
const AWS = require('aws-sdk');
const region = 'us-west-2';
const secretName = 'example/example/example';
let secrets = {
username: '',
password: '',
host: '',
port: '',
database: '',
email: 'example#example.com',
emailPassword: 'SomePassword'
};
const client = new AWS.SecretsManager({
region: region
});
client.getSecretValue({ SecretId: secretName }, async (err, data) => {
if (err) {
throw err;
} else {
const res = await JSON.parse(data.SecretString);
secrets.username = res.username;
secrets.password = res.password;
secrets.host = res.host;
secrets.port = res.port;
secrets.database = res.database;
}
});
module.exports = secrets;
Question
The obvious problem here is not creating the promise correctly but I am not sure why my attempts are completing the promises after the file gets exported. If I console.log(secrets) in another file in some cases it will output the object missing data then show the data returned by the promise a few seconds later after when I console.log(secrets) inside of the function.
What is the proper way to build this object secrets and export it once the data is returned from AWS and added to the object secrets?
According to the docs, the second argument to getSecretValue is a callback function, so there's no need to use async/await since async/await is meant to work with promises.
Removing async/await should work.
client.getSecretValue({ SecretId: secretName }, (err, data) => {
if (err) {
throw err;
} else {
const res = JSON.parse(data.SecretString);
secrets.username = res.username;
secrets.password = res.password;
secrets.host = res.host;
secrets.port = res.port;
secrets.database = res.database;
}
});
However, you're exporting the secrets object synchronously, and its properties are getting set asynchronously.
Instead, you can return a promise for your other modules to consume.
const AWS = require('aws-sdk');
const region = 'us-west-2';
const secretName = 'example/example/example';
let secrets = {
email: 'example#example.com',
emailPassword: 'SomePassword'
};
const client = new AWS.SecretsManager({
region: region
});
const promise = new Promise((resolve, reject) => {
client.getSecretValue({ SecretId: secretName }, async (err, data) => {
if (err) {
reject(err);
} else {
const res = await JSON.parse(data.SecretString);
secrets.username = res.username;
secrets.password = res.password;
secrets.host = res.host;
secrets.port = res.port;
secrets.database = res.database;
resolve(secrets);
}
});
})
module.exports = promise;
Then, in some other module that consumes this one, you can use async/await since we now have a promise.
import {promise} from "./this-module";
(async () => {
const secrets = await promise;
console.log(secrets);
})();
Update
I'm not sure if this will work, but it's worth a shot. Here, set module.exports only after secrets is set. If this doesn't work, then I would ask a new question on StackOverflow about how to export resolved promises with CommonJS (which is the module format that you're using).
const AWS = require('aws-sdk');
const region = 'us-west-2';
const secretName = 'example/example/example';
let secrets = {
email: 'example#example.com',
emailPassword: 'SomePassword'
};
const client = new AWS.SecretsManager({
region: region
});
const promise = new Promise((resolve, reject) => {
client.getSecretValue({ SecretId: secretName }, async (err, data) => {
if (err) {
reject(err);
} else {
const res = await JSON.parse(data.SecretString);
secrets.username = res.username;
secrets.password = res.password;
secrets.host = res.host;
secrets.port = res.port;
secrets.database = res.database;
resolve(secrets);
}
});
});
(async () => {
module.exports = await promise;
})();
client.getSecretValue({ SecretId: secretName }).subscribe(data=>{
const res =JSON.parse(data.SecretString);
secrets.username = res.username;
secrets.password = res.password;
secrets.host = res.host;
secrets.port = res.port;
secrets.database = res.database;
});

Categories

Resources