Calling External API in botkit - node.js

As I am new to botkit, I have created the bot locally as per the docs. But when it is trying to call an external API using axios, below error shows -
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'get' of undefined
Here is the code -
const { BotkitConversation } = require("botkit");
const { axios } = require("axios");
module.exports = function(controller) {
const convo = new BotkitConversation('convo', controller);
convo.before('default', async(convo, bot) => {
axios.get(...https url...) //error line
.then(function (response) {
convo.setVar('api-response', response);
})
.catch(function (error) {
console.log(error);
})
});
convo.ask('What is your name?', async(response, convo, bot) => {
console.log(`user name is ${ response }`);
}, 'name');
convo.addAction('msg');
convo.addMessage('Hi {{vars.name}}! Welcome to my Botkit', 'msg');
}
Can someone point out what is wrong on this code?

Related

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'session') at LogoutSession NodeJS

I have this router.delete in the Nodejs server that make the logout so the req.session enters full and then gets destroyed successfully the problem is when I use the res.send() to send the session or a message it doesn't work:
router.delete('/logout', async(req,res) => {
console.log(req.session)
req.session.destroy()
res.send({ session: req.session });
console.log(req.session)
res.end()
});
I try to display the data that comes from that router on the react frontend like this and it tells me the error that is written in the title:
const LogoutSession = async(e) =>{
e.preventDefault();
const response = await fetch(`http://localhost:3001/logout`, {
method:"DELETE",
})
console.log(response.data.session)
}
I found the solution, I used the method .json to send data to the frontend this way:
router.delete('/logout', async(req,res) => {
try {
console.log(sess)
sess = null
console.log(sess)
res.status(200).json({code: 200, message: 'loggedout', session1:
sess})
} catch (err) {
console.log(err)
}
res.end()
});
And then I used it like this in the frontend:
const LogoutSession = async(e) =>{
e.preventDefault();
const response = await fetch(`http://localhost:3001/logout`, {
method:"DELETE",
})
const data = await response.json();
if(data.session1 === null){
history.push('/')
}
}

In React 18, when I am sending data to nodejs server, it is sending two request and receiving two response from server

This is the front-end code which is used for sending access token to server site.
useEffect(() => {
const getProducts = async () => {
try {
const url = `http://localhost:5000/product?email=${user.email}`
const { data } = await axios.get(url, {
headers: {
authorization: localStorage.getItem('accessToken')
}
});
setProducts(data);
} catch (err) {
const status = err.response.status;
if (status === 401 || status === 403) {
signOut(auth);
navigate('/login');
localStorage.removeItem('accessToken')
toast.error(err.response?.data?.message);
}
}
}
getProducts();
}, [user.email]);
This is server site express code for response. Why every time it is receiving two request and sending two response?
app.get('/product', verifyToken, async (req, res) => {
const decoded = req.decoded?.email;
const queryEmail = req.query?.email;
if (decoded === queryEmail) {
const query = { email: queryEmail };
const cursor = medicineCollection.find(query);
const products = await cursor.toArray();
res.send(products);
} else {
res.status(403).send({ message: "Forbidden Access" })
}
})
Maybe you take user.email in a state which is updating somehow so that's why useEffect is calling again and giving you twice response.

Stripe Webhook fails to fire, write data to firebase

I have a webhook I'm using to write data to firebase when a stripe purchase happens, and I cant get it to work.
I've tried adding try/catch statements around everything, i cant seem to catch the error regardless.
Im using the nextJS backed debugging as found here:
https://nextjs.org/docs/advanced-features/debugging
My question is why? all my api keys are correct.
the error :
[ERROR] Failed to POST: Post "http://localhost:3000/api/webhook": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
The Webhook:
import { buffer } from "micro";
import * as admin from "firebase-admin";
//secure a connection to firebase
const serviceAccount = require("../../../permissions.json");
const app = !admin.apps.length
? admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
: admin.app();
//connect to stripe
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_SIGNING_SECRET;
const fullfillOrder = async (session) => {
try {
return app
.firestore()
.collection("users")
.doc(session.metadata.email)
.collection("orders")
.doc(session.id)
.set({
amount: session.amount_total / 100,
amount_shipping: session.total_details.amount_shipping / 100,
images: JSON.parse(session.metadata.images),
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
.then(() => {
console.log(`SUCCESS : Order ${session.id} has been added to DB.`);
});
} catch (error) {
console.error(error);
}
};
export default async (req, res) => {
try {
} catch (error) {
console.error(error);
}
if (req.method === "POST") {
const requestBuffer = await buffer(req);
const payload = requestBuffer.toString();
const sig = req.headers["stripe-signature"];
let event;
//verify that event posted from stripe
try {
event = stripe.webhooks.constructEvent(payload, sig, endpointSecret);
} catch (err) {
return res.status(400).send(`Webhook error : ${err.message}`);
}
//Handle checkout session completed event
try {
if (event.type === "checkout.session.completed") {
const session = event.data.object;
return fullfillOrder(session)
.then(() => res.status(200))
.catch((err) =>
res.status(400).send(`Webhook Error :${err.message}`)
);
}
} catch (error) {
console.log("🚀 ~ file: webhook.js ~ line 56 ~ error", error);
}
}
};
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
};

What is the proper way to execute axios / firebase promises in a specific order in a firebase function?

What is the best way to chain axios / firebase promises that must be linked in a specific order and use the returns of previous promises?
I am writing a firebase function that allows me to update a user via a third-party JWT API. So I have to fulfill several promises (I use axios for that) to build the final query with a uid, a token and a refresh token.
These requests must be executed in the right order, each promise waiting for the result of the previous one to be able to execute.
recover the firebase client token to identify the user
search in a collection for the tokens (access & refresh) that were previously stored and associated with the user's uid.
Execute the "me" request on the third-party API to retrieve the user's information and update the user.
My question: What is the most correct way to chase these axios promises?
For the moment, I have managed to achieve this result, by interlocking the calls successively to properly manage the "catch" and by moving in separate functions the calls to make a little more digest the reading of the code.
/* index.js */
const userModule = require('./user');
exports.me = functions.https.onRequest( (request, response) => {
cors(request, response, () => {
let idToken = request.body.data.token;
userModule
.get(idToken)
.then((uid) => {
console.log('User found : ' + uid);
return userModule
.retrieve(uid)
.then((userTokens) => {
console.log('User tokens found : ' + userTokens.token);
return userModule
.me(userTokens.token, uid)
.then((me) => {
return me;
}).catch((error) => {
return response.status(404).json({
data : {
error : 404,
message : 'NO_USER_ON_API'
}
});
})
}).catch((error) => {
console.log(error);
return response.status(404).json({
data : {
error : 404,
message : 'NO_TOKEN_USER_FOUND'
}
});
})
})
.catch((error) => {
console.log(error);
return response.status(500).json({
data : {
error : 500,
message : 'USER_TOKEN_NO_MATCH'
}
});
})
.then((user) => {
if(user.data !== undefined)
{
return response.status(200).json({
data : {
user : user.data
}
});
}
else
{
return response.status(204).json({
data : {
user : null
}
});
}
})
});
});
/* user.js */
exports.get = (firebaseToken) {
return admin.auth().verifyIdToken(firebaseToken)
.then(function(decodedToken) {
return decodedToken.uid;
})
.catch(function(error) {
throw {
code: 500,
body: "INTERNAL_ERROR"
};
});
};
exports.retrieve = (uid) {
return admin.firestore().collection("AccessTokenCollection").doc(uid).get()
.then(function(docRef) {
return docRef.data();
})
.catch(function(error) {
throw {
code: 404,
body: "NO_USER_FOUND"
};
});
};
exports.me = (UserToken, uid) {
let params = {
params: {
},
headers: {
'Authorization': 'Bearer ' + UserToken
}
};
return axiosInstance.instance.get(url + '/users/me', params)
.then(userMe => {
return userMe;
})
.catch(errMe => {
console.log(errMe.response.status);
throw {
code: 401,
body: "EXPIRING_TOKEN"
};
});
};
Etc...
The code works as it is more a theoretical question or optimization!
const userModule = require('./user');
exports.me = functions.https.onRequest((request, response) => {
cors(request, response, async () => {
let idToken = request.body.data.token;
try {
let uid = await userModule.get(idToken);
console.log('User found : ' + uid);
let userTokens = await userModule.retrieve(uid);
console.log('User tokens found : ' + userTokens.token);
let meObj = await userModule.me(userTokens.token, uid);
} catch (error) {
console.log('error', error);
}
});
});
So, here using async-await i have removed then-catch block. await keyword will work as then and will only move forward to second call after first call has been completed. And i have made a common catch block for error handling which you can modified according to your needs
you can use promise.all and async-await instead of then and catch

Promise only resolves on the first request

I am developing an API to create a warehouse structure. Because we are using a microservice architecture I need to make a request via rabbitmq to another microservice to generate the address for the new warehouse.
Therefore I use the ampq consume function wrapped in a function which returns a promise. When I hit the endpoint the first time the promise gets resolved and I can continue with my data. But in the second request, the promise will not get resolved.
Maybe it's for an obvious reason but at the moment I don't get it.
So here is my code:
routes.js
router.post('/', (req, res) => {
...
const validate = ajv.compile(warehoseSchema);
const valid = validate(req.body);
if (valid) {
sendToQueue('addressMgmt', req.body.address);
consume()
.then((result) => {
const {
id_address: idAddress,
license_plate: licensePlate,
town,
} = result.body;
createWarehouseHandler(
customernumber, req.body, idAddress, licensePlate, town,
)
.then((insertId) => {
res.json({
id: 'warehouses02',
message: `Warehouse with id ${insertId} successfully created`,
});
})
.catch((err) => {
res.status(err.status).json({
id: err.id,
message: err.message || err.sqlMessage,
});
});
}).catch((err) => {
res.status(err.status).json({
id: err.id,
message: err.message || err.sqlMessage,
});
});
} else {
res.status(417).json({
id: 'warehouses01',
message: `Invalid JSON: ${ajv.errorsText(validate.errors)}`,
});
}
});
const consume = () => new Promise((resolve, reject) => {
const q = 'warehouseMgmt';
amqpCh.consume(q, (msg) => {
const message = JSON.parse(msg.content.toString());
if (Object.keys(message).includes('body')) {
resolve(message);
} else {
const err = new Error();
err.status = 500;
err.id = 'rabbit01';
err.message = 'No message was cosumed';
reject(err);
}
}, { noAck: true });
});
On the first request consume().then() gets called but on the second and following requests, it doesn't.
Thanks for your help

Resources