Get value of variable outside API function call [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 10 months ago.
I am trying to make a chat application project with abusive text detection. I found code for the chat application online and want to add text detection using Perspective API. The API has several attributes for toxicity, threat etc. I am able to set the attributes inside the API function but I am unable to access them outside it.
Here is the relevant code:-
const sendMessage = asyncHandler(async (req, res) => {
const { content, chatId } = req.body;
let toxicity, insult, profanity, threat;
if (!content || !chatId) {
console.log("Invalid data passed into request");
return res.sendStatus(400);
}
let newMessage = {
sender: req.user._id,
content: content,
chat: chatId,
toxicity: toxicity,
insult: insult,
profanity: profanity,
threat: threat,
};
let inputText = newMessage.content;
// Perspective API
google
.discoverAPI(process.env.DISCOVERY_URL)
.then((client) => {
const analyzeRequest = {
comment: {
text: inputText,
},
requestedAttributes: {
TOXICITY: {},
INSULT: {},
PROFANITY: {},
THREAT: {},
},
};
client.comments.analyze(
{
key: process.env.API_KEY,
resource: analyzeRequest,
},
(err, response) => {
if (err) throw err;
// console.log(JSON.stringify(response.data, null, 2));
toxicity = (response.data.attributeScores.TOXICITY.summaryScore.value * 100).toFixed(2);
insult = (response.data.attributeScores.INSULT.summaryScore.value * 100).toFixed(2);
profanity = (response.data.attributeScores.PROFANITY.summaryScore.value * 100).toFixed(2);
threat = (response.data.attributeScores.THREAT.summaryScore.value * 100).toFixed(2);
newMessage.toxicity = toxicity;
newMessage.insult = insult;
newMessage.profanity = profanity;
newMessage.threat = threat;
console.log("1-" + newMessage.toxicity); // This returns the desired output
}
);
})
.catch((err) => {
throw err;
});
//
console.log("2-" + newMessage.toxicity); // This returns undefined
try {
let message = await Message.create(newMessage);
message = await message.populate("sender", "name profilePic");
message = await message.populate("chat");
message = await User.populate(message, {
path: "chat.users",
select: "name profilePic email",
});
await Chat.findByIdAndUpdate(req.body.chatId, {
latestMessage: message,
});
res.json(message);
} catch (error) {
res.status(400);
throw new Error(error.message);
}
});
I want newMessage to be updated after the API call. After coming across this post, I found that console.log("2-" + newMessage.toxicity) executes before console.log("1-" + newMessage.toxicity). I tried using callbacks and async/await but couldn't make it work.

The console.log("2-" + newMessage.toxicity) is outside the google.discoverAPI call so it execute instantly.
you can try something like this
const sendMessage = asyncHandler(async (req, res) => {
const { content, chatId } = req.body;
let toxicity, insult, profanity, threat;
if (!content || !chatId) {
console.log("Invalid data passed into request");
return res.sendStatus(400);
}
let newMessage = {
sender: req.user._id,
content: content,
chat: chatId,
toxicity: toxicity,
insult: insult,
profanity: profanity,
threat: threat,
};
let inputText = newMessage.content;
// Perspective API
const client = await google
.discoverAPI(process.env.DISCOVERY_URL)
const analyzeRequest = {
comment: {
text: inputText,
},
requestedAttributes: {
TOXICITY: {},
INSULT: {},
PROFANITY: {},
THREAT: {},
},
};
await new Promise((resolve, reject) => {
client.comments.analyze(
{
key: process.env.API_KEY,
resource: analyzeRequest,
},
(err, response) => {
if (err) {
reject(err)
}
// console.log(JSON.stringify(response.data, null, 2));
toxicity = (response.data.attributeScores.TOXICITY.summaryScore.value * 100).toFixed(2);
insult = (response.data.attributeScores.INSULT.summaryScore.value * 100).toFixed(2);
profanity = (response.data.attributeScores.PROFANITY.summaryScore.value * 100).toFixed(2);
threat = (response.data.attributeScores.THREAT.summaryScore.value * 100).toFixed(2);
newMessage.toxicity = toxicity;
newMessage.insult = insult;
newMessage.profanity = profanity;
newMessage.threat = threat;
console.log("1-" + newMessage.toxicity);
resolve()
}
);
})
.catch((err) => {
throw err;
});
//
console.log("2-" + newMessage.toxicity); // This returns undefined
try {
let message = await Message.create(newMessage);
message = await message.populate("sender", "name profilePic");
message = await message.populate("chat");
message = await User.populate(message, {
path: "chat.users",
select: "name profilePic email",
});
await Chat.findByIdAndUpdate(req.body.chatId, {
latestMessage: message,
});
res.json(message);
} catch (error) {
res.status(400);
throw new Error(error.message);
}
});

Related

Getting erros using passport-google-oauth20 InternalOAuthError: Failed to fetch user profile and Cannot set headers after they are sent to the client

I'm using passport strategies for different socialMedia logins and getting the following two errors
InternalOAuthError: Failed to fetch user profile
Cannot set headers after they are sent to the client
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
strategy code
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_SECRET_KEY,
callbackURL: GOOGLE_CALLBACK_URL
}, async (acessToken, refreshToken, profile, done) => {
await User.findOne({ email: profile._json.email }, async (err, user) => {
if (err) {
console.log("passport.config --> err", err);
done(err, null);
} else if (user) {
if (user.socialType !== "GOOGLE" || user.socialType === null)
done(`LOGIN_CREDENTIALS_WITH_${(user.socialType || "PASSWORD").toUpperCase()}`, false);
else {
done(null, user);
}
} else {
// console.log(profile);
const user = {
email: profile._json.email,
socialId: profile.id,
socialType: "GOOGLE",
firstName: profile.name.givenName,
lastName: profile.name.familyName,
isActive: profile._json.email_verified,
isVerified: profile._json.email_verified,
socialImageUrl: profile._json.picture,
userType: "CUSTOMER"
};
const newUser = new User({ ...user });
const newUserData = await newUser.save();
done(null, newUserData);
}
});
}));
route code:
router.get('/auth/:socialType', customerCtrl.socialTypeLogin);
router.get('/auth/:socialType/callback', customerCtrl.socialTypeLoginCallback);
controller code:
const socialTypeLogin = async (req, res) => {
await customerService.socialTypeLogin(req, res);
};
const socialTypeLoginCallback = async (req,res) => {
await customerService.socialTypeLoginCallback(req,res);
};
service code:
const socialTypeLogin = async (req, res) => {
try {
const socialType = (req.params.socialType || '').toLowerCase();
const GOOGLE_SCOPE = ['email', 'profile'];
const FACEBOOK_SCOPE = ['email'];
let scope = [];
if (socialType === 'google') {
scope = GOOGLE_SCOPE;
} else if (socialType === 'facebook') {
scope = FACEBOOK_SCOPE;
}
let oauthOptions = { scope: scope};
const { returnUrl } = req.query;
if(returnUrl && returnUrl.trim().length !== 0) {
oauthOptions['state'] =JSON.stringify({ returnUrl: returnUrl });
}
passport.authenticate(socialType, oauthOptions)(req, res);
}
catch (error) {
}
}
/**
* #param {string} socialType
*/
const socialTypeLoginCallback = async (req, res) => {
const socialType = (req.params.socialType || '').toLowerCase();
// return new Promise((resolve, reject) => {
try {
passport.authenticate(socialType, async (err, user) => {
let webappRedirectURL = WEBAPP_LOGIN_URL;
try {
const state = req.query.state;
if(state) {
const stateObj = JSON.parse(state);
webappRedirectURL = stateObj.returnUrl;
}
} catch (err1) {
console.log("customer.service --> parsing error",err1);
}
if (err || !user) {
console.log("customer.service --> !user",err);
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: false,
error: err,
timerCounter: 5,
accessToken: undefined
});
}
else {
console.log("customer.service --> Generating Token",user.generateJWT());
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: true,
timerCounter: 5,
accessToken: user.generateJWT(),
error: undefined
});
}
})(req, res);
}
catch (error) {
console.log("customerService.js ==> socialTypeLoginCallback -->",error);
}
};
Thanks for help in advance!
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
In socialTypeLogin
add line
oauthOptions['session'] = false;

Output results to the user

I'm writing a telegram bot. There is a piece of working code that responds to messages from the user, searches for key word matching database and sends the result to user. The problem is that the sample result gets into the console, how to send it to the user? Please, help
bot.on('message', (ctx) => {
const text = ctx.text
const log = sequelize.query("SELECT book FROM books t WHERE (t.*)::text LIKE '%"+ text +"%'") .then( (result) => {
console.log(result,log)
}) .catch( (err) => {
console.log(err);
for (const result of results) {
ctx.reply(result.book);
}
})
})
Based on sendMessage api and data in message Your code should look like this:
const { QueryTypes } = sequelize;
bot.on('message', async (message) => {
const {text, chat} = message; // https://core.telegram.org/bots/api#message
const {id: chatId} = chat; // https://core.telegram.org/bots/api#chat
let response = '';
try {
const rows = await sequelize.query(
'SELECT book FROM books t WHERE (t.*)::text LIKE :searchText',
{
replacements: { searchText: `%${text}%` },
type: QueryTypes.SELECT,
}
);
console.log('ROWS:', rows);
if (rows.length) {
response = rows.map(row => row.book).join("\n");
}
else {
response = 'Book not found';
}
}
catch (error) {
console.error(error.message);
response = 'Unable to lookup';
}
finally {
if (response) {
bot.sendMessage(chatId, response);
}
}
})
Check manuals:
sendMessage
Message object
Chat object
Sequelize replacements

Async/Await : called function is not awiting and returning value

Hello I am creating 1 function with dynamic arguments where as I am calling api and on defined route I am calling express middleware function and from there I am calling another dynamic function which will help me to insert data into the database.
I am using Sequalize ORM
Here is code:
var async = require('async');
// Models
var LogSchema = require('../models/Logs')
module.exports = {
insertLog: async (req, res) => {
let result = await insertLogFn('1', 'method_name()', 'module_name_here', 'req.body', '{ api response }', 'action', '24')
console.log("result", result)
res.status(200).json(result)
}
};
function insertLogFn(status, invokedMethodName, moduleName, bodyRequest, apiResponse = null, actionName = null, userId) {
async.waterfall([
(nextCall) => {
let dataToBeInserted = {}
dataToBeInserted.status = status,
dataToBeInserted.invoked_method_name = invokedMethodName,
dataToBeInserted.module_name = moduleName,
dataToBeInserted.body_request = bodyRequest,
dataToBeInserted.api_response = apiResponse
dataToBeInserted.action_name = actionName,
dataToBeInserted.user_id = userId
LogSchema.create(dataToBeInserted).then(res => {
const dataObj = res.get({plain:true})
nextCall(null, {
status: 200,
message: "Log inserted successfully",
data: dataObj
})
}).catch(err => {
})
}
], (err, response) => {
if(err) {
}
return response
})
}
In module.export I have added insertLog function which is getting called in api and from there I am calling insertLogFn() which is declared outside of the module.export.
I am able to get inserted result in function insertLogFn() but the things is await is not working and not waiting for the result.
What I want to do is to wait till insertLogFn gets executed and the returned response has to be stored in the variable and return it as an api response.
You cannot. As per my understanding, IMO, Thumb rule is "Async/Await operation should return a promise"
function insertLogFn(status, invokedMethodName, moduleName, bodyRequest, apiResponse = null, actionName = null, userId) {
async.waterfall([
(nextCall) => {
let dataToBeInserted = {}
dataToBeInserted.status = status,
dataToBeInserted.invoked_method_name = invokedMethodName,
dataToBeInserted.module_name = moduleName,
dataToBeInserted.body_request = bodyRequest,
dataToBeInserted.api_response = apiResponse
dataToBeInserted.action_name = actionName,
dataToBeInserted.user_id = userId
LogSchema.create(dataToBeInserted).then(res => {
const dataObj = res.get({plain:true})
nextCall(null, {
status: 200,
message: "Log inserted successfully",
data: dataObj
})
return ;
console.log("you should return something here<-------");
}).catch(err => {
})
}
], (err, response) => {
if(err) {
}
return response
})
}
Now the answer will be clear if you read this one from Bergi: https://stackoverflow.com/a/40499150/9122159

Express dosn't get return of other function querying Mongodb [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I'm working in a simple API Key authentication, I just want to verify the given key against the user provied key.
I have a seperate file with the function querying the database, and returning true/false and the user object.
But in my route.js file, the return object is undefined even tough in my auth.js file it isn't.
I tried making the the function in router.get an async function using express-promise-router and making the function an await return var user = await auth.verify(req.params.uid, req.get("token")) but I don't realy know how async works.
router.js
[...]
router.get('/list/:uid', function(req, res) {
var user = auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user) // <-- Undefined
if (user.status) {
res.send("Success")
} else {
res.status(403)
res.json({status: 403, error: "Unkown User / Token"})
}
})
[...]
auth.js
var db = require('./db')
var ObjectId = require('mongodb').ObjectId;
module.exports = {
verify: (uid, key) => {
try {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {throw err}else{status=1}
if (user.length <= 0) {throw "NotExistingExc"; status = 0}else{
usr = user[0];
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
} /* --> Is {
status: 1,
user: {
_id: d47a2b30b3d2770606942bf0,
name: 'Sh4dow',
groups: [ 0 ],
api: 'YWFiMDI1MGE4NjAyZTg0MWE3N2U0M2I1NzEzZGE1YjE='
}
}
*/
return returnObj;
})
} catch (e) {
console.error(e)
return {
status: 0,
user: {},
error: e
}
}
}
}
db.js (Idk if needed)
var MongoClient = require('mongodb').MongoClient
var state = {
db: null,
}
exports.connect = function(url, done) {
if (state.db) return done()
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) return done(err)
state.db = db
done()
})
}
exports.get = function() {
return state.db.db("database")
}
exports.close = function(done) {
if (state.db) {
state.db.close(function(err, result) {
state.db = null
state.mode = null
done(err)
})
}
}
I want to have the returnObjin auth.js in the router.get of my route.js file.
Make auth.verify return a Promise which we can then await for it inside router, You can just make the callback async no need for express-promise-router
router.get('/list/:uid', async function(req, res) {
try {
var user = await auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user)
if (user.status) {
res.send("Success")
} else {
res.status(403).json({status: 403, error: "Unkown User / Token"})
}
} catch (e) {
console.error(e)
res.status(/* */).json(/* */)
}
})
auth
module.exports = {
verify: (uid, key) => new Promise((resolve, reject) => {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {
reject(err)
return
} else {
status = 1
}
if (user.length <= 0) {
reject(new Error("NotExistingExc"))
return
} else {
usr = user[0]
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
}
resolve(returnObj);
})
}
}
In short, the reason you get undefined is because the code in auth.js is asyncronous. But you're really close. The toArray method in MongoDB returns a promise, so you need to make sure you return that promise and then use it in the router correctly.
In auth.js, make sure verify returns a promise - just add return!
return collection.find(query).limit(1).toArray(...)
And then, change your usage of the verify to the async/await you originally tried:
router.get('/list/:uid', async function(req, res) {
var user = await auth.verify(req.params.uid, req.get("token"))
// More code here...
})

Firebase Cloud Functions with Cloud Firestore trouble

I have used this Firebase Database code in a previous project:
const getDeviceUser = admin.database().ref(`/users/${notification.to}/`).once('value');
I am now trying to convert it for Firestore. I am basically trying to get my users fcm's when a notification is being sent. I have tried many things, but haven't seen the new way to accomplish this.
EDIT: here is my code.
exports.sendFavoriteNotification = functions.firestore.document('users/{userUid}/notifications/{notificationId}').onCreate(event => {
const notification = event.data.data();
const user = event.params.userUid;
const getDeviceUser = admin.database().ref(`/users/${notification.to}/`).once('value');
// Get the follower profile.
const getProfilePromise = admin.auth().getUser(notification.sender);
return Promise.all([getDeviceUser, getProfilePromise]).then(results => {
const tokensSnapshot = results[0];
const liker = results[1];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
//console.log('There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.');
console.log('Fetched follower profile', liker);
// Notification details.
const payload = {
notification : {
title : 'You have a new like!',
body : `${liker.displayName} just liked your photo.`,
badge: '1',
sound: 'default'
}
};
// Listing all tokens.
var tokens = admin.firestore.ref(`/users/${notification.to}/`).get('fcm');
// Send notifications to all tokens.
admin.messaging().sendToDevice(tokens.data(), payload);
return admin.messaging().sendToDevice(tokens, payload).then(response => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.update({
fcm: FieldValue.delete()
}));
}
}
});
return Promise.all(tokensToRemove);
});
});
});
Hope this will help. This is my code after 2 days of trying to learn how to convert from realtime database to firestore. It is based on a firebase project: https://github.com/MahmoudAlyuDeen/FirebaseIM
let functions = require('firebase-functions');
let admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotificationToFirestone = functions.firestore.document('/notifications/{pushId}')
.onCreate(event => {
const pushId = event.data.id;
const message = event.data.data();
const senderUid = message.from;
const receiverUid = message.to;
const db = admin.firestore();
if (senderUid === receiverUid) {
console.log('pushId: '+ pushId);
return db.collection('notifications').doc(pushId).delete();;
} else {
const ref = db.collection('users').doc(receiverUid);
const query = new Promise(
function (resolve, reject) {
ref.get()
.then(doc => {
if (!doc.exists) {
console.log('No such document!');
reject(new Error('No such document!'));
} else {
console.log('Document data:', doc.data().instanceId);
resolve(doc.data().instanceId);
}
})
.catch(err => {
console.log('Error getting document', err);
reject(err);
});
});
const getSenderUidPromise = admin.auth().getUser(senderUid);
return Promise.all([query, getSenderUidPromise]).then(results => {
//console.log('instanceId = Result[0]: ' + results[0]);
//console.log('sender = Result[1]: ' + results[1]);
const instanceId = results[0];
const sender = results[1];
//console.log('notifying ' + receiverUid + ' about ' + message.body + ' from ' + senderUid);
//console.log('instanceId este' + instanceId);
const payload = {
notification: {
title: sender.displayName,
body: message.body,
icon: sender.photoURL
}
};
admin.messaging().sendToDevice(instanceId, payload)
.then(function (response) {
console.log("Message sent: ", response);
})
.catch(function (error) {
console.log("Error sending message: ", error);
});
});
}
});

Categories