Getting single message from Graph - javascript

I'm trying to get a single email from an Office 365 Mailbox.
I'm sending the email id to my app via a POST (req.body.id) and then calling this code in order to get some email properties:
router.post('/id', async function(req, res, next) {
console.log("email with ID -> ", req.body.id)
let parms = { title: 'Inbox', active: { inbox: true } };
const accessToken = await authHelper.getAccessToken(req.cookies, res);
const userName = req.cookies.graph_user_name;
if (accessToken && userName) {
parms.user = userName;
// Initialize Graph client
const client = graph.Client.init({
authProvider: (done) => {
done(null, accessToken);
}
});
try {
const result = await client
.api('/me/messages/', req.body.id)
.select('id,subject,from,toRecipients,ccRecipients,body,sentDateTime,receivedDateTime')
.get();
parms.messages = result.value;
console.log("email -> ", result.value);
res.render('message', parms);
} catch (err) {
parms.message = 'Error retrieving messages';
parms.error = { status: `${err.code}: ${err.message}` };
parms.debug = JSON.stringify(err.body, null, 2);
res.render('error', parms);
}
} else {
// Redirect to home
res.redirect('/');
}
});
At the moment, result.value contains all of the messages in the mailbox instead of just the message with provided id.
Could someone tell me where my error is, please?

The api method has a single path parameter. Calling it like .api('/me/messages/', req.body.id) is effectivly sending it a path ("/me/messages/") along with an additional parameter it ignores.
You need to send it a single string so you'll need to append the req.body.id to the path ({path} + {id}):
const result = await client
.api('/me/messages/' + req.body.id)
.select('id,subject,from,toRecipients,ccRecipients,body,sentDateTime,receivedDateTime')
.get();

Related

Why does a put after my post, remove the last added data in my json file

In my backend is something happening, which I can't understand. If I'm registering a new User, it's working fine, and I can see the new User in my JSON File, but if I'm doing a put request after that to change my own user's data he deletes the new User which I made before?
My put request from my frontend:
//Changing user Data
export async function changeData(id, body) {
try {
await axios.put(`http://localhost:8000/users/${id}`, body, {
headers: {
'Content-Type': 'application/json',
'Authorization': localStorage.getItem('auth._token.local')
}
});
return true;
}
catch (e) {
return false;
}
}
My endpoint in my node backend for registering a user and changing data of a user
// Register New User
server.post('/register', (req, res) => {
console.log("register with request body", req.body)
const {username, password, firstname, lastname, roles} = req.body
if(!username || !password || !firstname || !lastname || !roles) {
const status = 400
const message = "Bad Request, make sure all properties are set in request body"
res.status(status).json({status, message})
return
}
if (req.headers.authorization === undefined || req.headers.authorization.split(' ')[0] !== 'Bearer') {
const status = 401
const message = 'Error in authorization format'
res.status(status).json({status, message})
return
}
// Send only token part to admin check
if(!isAdmin(req.headers.authorization.split(' ')[1])) {
const status = 401
const message = 'Only permitted by admin'
res.status(status).json({status, message})
return
}
if(isAuthenticated({username, password}) === true) {
const status = 401
const message = 'Email and Password already exist'
res.status(status).json({status, message})
return
}
fs.readFile("./users.json", (err, file) => {
if (err) {
const status = 401
const message = err
res.status(status).json({status, message})
return
}
// Get current users data
const data = JSON.parse(file.toString())
// Get the id of last user
const last_item_id = data.users[data.users.length - 1].id
//Add new user
data.users.push({id: last_item_id + 1, username: username, password: password, firstname: firstname, lastname: lastname, roles: roles}) //add some data
const writeData = fs.writeFile("./users.json", JSON.stringify(data), (err, result) => { // WRITE
if (err) {
const status = 401
const message = err
res.status(status).json({status, message})
}
})
})
res.status(201).json({status: 201, message: "Successfully created"})
})
// handle changing user data
server.use((req, res, next) => {
console.log('Entering Users')
if(req.method === 'PUT' && req.url.includes("/users")) {
if(req.body) {
const decodedToken = jwt.decode(req.headers.authorization.split(' ')[1])
const userList = JSON.parse(fs.readFileSync('./users.json', 'UTF-8'))
const userinfo = userList.users.find((user) => user.id === decodedToken.id)
if(!req.body.password) {
req.body.password = userinfo.password
}
// if admin made the request, he should be able to change roles
if(req.body.roles && decodedToken.roles && decodedToken.roles.includes("admin")) {
console.log("Able to change");
next()
return
}
req.body.roles = decodedToken.roles
} else {
res.status(400).json(
{
status: 400,
message: "Bad request, make sure all properties are set in request body"
}
)
return
}
}
next()
})
The only thing i noticed is that after the register comes, the JSON file gets to a one-liner, but I don't think that this is the problem. It seems like the put works with an old user List? I'm not sure. Thanks in forward.

Code continuing after response already sent

I am trying to write a test for sending a 404 error whenever a blog is posted without a token and I keep getting "TypeError: Cannot read properties of undefined (reading '_id')". I know this is because my user variable is null which is due to there being no token sent with the request. Shouldn't the code stop reading after the response is sent? Or is there another way to counteract this? User will only be defined when a token is sent with the request. I can sort of solve this by using if/else statements to only use code if the user var is defined but is there a more effective way? Any help would be greatly appreciated.
const blogRouter = require('express').Router();
const req = require('express/lib/request');
const Blog = require('../models/blog')
const User = require('../models/user')
const jwt = require('jsonwebtoken');
const { default: mongoose } = require('mongoose');
const { decode } = require('jsonwebtoken');
require('dotenv').config()
blogRouter.post('/', async (request, response) => {
console.log("Request body of blog is",request.body)
var user = request.user
console.log("User is", user)
try{
const decodedToken = jwt.verify(request.token, process.env.SECRET)
console.log("decodedToken is", decodedToken)
}
catch(err){
if(!request.token || !decodedToken.id){
console.log("got in the catch block")
response.status(401).json({error: "token missing or invalid"})
}
else {
console.log(err)
}
}
const body = request.body
const newBlog = new Blog({
title: body.title,
author:body.author,
url: body.url,
likes: body.likes,
user: user._id // Error here because user is null
})
console.log("newBlog is",newBlog)
if (newBlog.title == null && newBlog.url == null){
response.status(400)
response.end()
}
else{
newBlog.save()
user.blog = user.blog.concat(newBlog._id)
user.save()
.then(result => {
console.log("Saved to database!!!!!")
response.status(201).json(result)
})
.catch(error => {
console.log("Could not save to database")
})
}
})
module.exports = blogRouter

Send data from React Native front end to Express back end

I'm new to React and building a MERN stack. I'm having issues passing a variable from my front end to my back end. I've tried using console logs to debug and I can see that my request body is coming up blank. I've spent hours trying to figure out what I'm doing wrong but I haven't had any breakthrough yet.
Please see my code below.
User Frontend Hook
const fetchUser = (dispatch) => {
return async () => {
const email = await AsyncStorage.getItem("email");
console.log("async email:", email);
try {
console.log("sending email:", email);
const userInfo = await trackerApi.get("/users", {email});
dispatch({ type: "fetch_users", payload: userInfo.data });
} catch (err) {
console.log(err);
}
};
};
Express/Axios Backend
router.get("/users", async (req, res) => {
console.log("Request Body:", req.body);
try {
const { email } = req.body;
// console.log("Email for req: ", email);
const user = await User.find({ email: email });
console.log("Users for req: ", user);
res.send(user);
} catch (err) {
console.log(err);
}
});
The issue is related to the HTTP method, your route/API is GET call and get method does not have the body, either update to post or use req.query.
Client
const userInfo = await trackerApi.post("/users", {email});
// OR
const userInfo = await trackerApi.post("/users", { data: {email});
Server
router.post("/users", async (req, res) => {

How do I correctly await the return of an async function in javascript?

I'm currently working on a Google Sign-in Auth app with a React frontend and an Express backend and I'm currently stuck in the part of the process where I'm validating tokens on the backend. The docs for this process show this code to validate the token:
const {OAuth2Client} = require('google-auth-library');
...
const client = new OAuth2Client(CLIENT_ID);
async function verify() {
const ticket = await client.verifyIdToken({
idToken: token,
audience: CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend
// Or, if multiple clients access the backend:
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
});
const payload = ticket.getPayload();
const userid = payload['sub'];
// If request specified a G Suite domain:
//const domain = payload['hd'];
}
verify().catch(console.error);
I've implemented this code in my own project here:
//verify token
async function verify(token, client) {
const ticket = await client.verifyIdToken({
idToken: token,
audience: keys.google.clientID,
});
const payload = ticket.getPayload();
const userid = payload['sub'];
const domain = payload['hd'];
const email = payload['email']
console.log('User ID: ' + userid);
console.log('Domian: ' + domain);
console.log('Email: ' + email);
var message = '';
var cookie = {};
await User.find({email: email}, (error, user) => {
if(error) {
message = error;
} else if (user.length === 0) {
message = 'this user is not in the database';
} else {
message = 'this user is in the database';
const session = new Session({
email: email,
session_token: token
});
cookie = {
email: email,
session_token: token
};
session.save((error, session) => {
if (error) {
console.log(error);
} else {
console.log('session saved');
}
});
console.log(message);
}
});
return Promise.resolve(cookie);
}
//recieve token id from frontend, verify it, and send session back in response
router.post('/google', (req, res) => {
const body = req.body.tokenID;
const client = new OAuth2Client(keys.google.clientID);
let cookie = verify(body, client).catch(console.error);
console.log('Cookie:' + cookie);
return res.send(cookie);
});
Currently when this runs everything inside the async function executes, but the return statement only returns the empty promise object. I think I'm making a mistake using async and await incorrectly, but I don't know how to correctly get the function to wait for all the work verifying the token and then update the DB before returning.
Not sure if this will help, but when I call the route my console gives me this output:
(I took out my personal info from the output fields, but assume these lines actually have gmail account info)
...
Cookie:[object Promise]
User ID: <GOOGLE ID>
Domian: <DOMAIN>
Email: <USER EMAIL>
this user is in the database
session saved
Thanks for reading!
Since "verify" function is an async function, you should add "await" before calling it. For catching errors you can simply place it in a try/catch:
router.post('/google', async (req, res) => {
const body = req.body.tokenID;
const client = new OAuth2Client(keys.google.clientID);
try {
let cookie = await verify(body, client);
console.log('Cookie:' + cookie);
return res.send(cookie);
} catch(error) {
// handling error
console.log(error);
return res.send("error")
}
});
`
You're mixing async/await with callback based calls. I don't know the internals of the library you're using, but the pattern should look more like this:
var cookie = {};
try{
const user = await User.find({email: email});
if (user.length === 0) {
console.log('this user is not in the database');
}
else {
console.log('this user is in the database');
const session = new Session({
email: email,
session_token: token
});
try{
await session.save();
console.log('session saved');
} catch(err){
console.log(err);
}
return {
email: email,
session_token: token
};
} catch(error){
console.log(error);
}

Firebase function to fetch data from Firebase DB to make Push notification

I have chat app with firebase database and Firebase cloud messaging. I can send firebase notification via console but in real scenario it should be automatic. To make automatic notification,My friend wrote Index.js (Added in cloud functions) file for me but its not sending notifications.
As per our logic function should trigger whenever there is any new entries (in any node or in any room) and fetch these values by firebase function and make post request to FCM server to make notification to receiver device (get value of receiver device from token_To).
Message
Message_From
Time
Type
token_To
Index.js
var functions = require('firebase-functions');
var admin = require('firebase-admin');
var serviceAccount = require('./demofcm-78aad-firebase-adminsdk-4v1ot-2764e7b580.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://demofcm-78aad.firebaseio.com/"
})
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
exports.setUserNode = functions.auth.user().onCreate(event => {
// ...
});
exports.notifyMsg = functions.database.ref('/{chatroom}/{mid}/')
.onWrite(event => {
if (!event.data.val()) {
return console.log('Message Deleted');
}
const getDeviceTokensPromise = admin.database().ref('/{chatroom}/{mid}/token_to').once('value');
return Promise.all([getDeviceTokensPromise]).then(results => {
const tokensSnapshot = results[0];
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
const payload = {
notification: {
title: 'You have a new Message!',
body: event.data.val().Message
}
};
const tokens = Object.keys(tokensSnapshot.val());
return admin.messaging().sendToDevice(tokens, payload).then(response => {
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
});
});
Firebase function Log
How can i fetch above mentioned values of any newly added node in same room(9810012321-9810012347) or any other room(9810012321-9810012325) from database and send it to FCM to make notification
Thanks in Advance.
What i did is created a Message node and I believe doing this by users key. ie, having the receiver(toId) and sender (fromId) key to send the notification.
Hope it helps.
exports.sendMessageNotification = functions.database.ref('/messages/{pushId}')
.onWrite(event => {
let message = event.data.current.val();
console.log('Fetched message', event.data.current.val());
let senderUid = message.fromId;
let receiverUid = message.toId;
let promises = [];
console.log('message fromId', receiverUid);
console.log('catch me', admin.database().ref(`/users/${receiverUid}`).once('value'));
if (senderUid == receiverUid) {
//if sender is receiver, don't send notification
//promises.push(event.data.current.ref.remove());
return Promise.all(promises);
}
let messageStats = message.messageStatus;
console.log('message Status', messageStats);
if (messageStats == "read") {
return Promise.all(promises);
}
let getInstanceIdPromise = admin.database().ref(`/users/${receiverUid}/pushToken`).once('value');
let getSenderUidPromise = admin.auth().getUser(senderUid);
return Promise.all([getInstanceIdPromise, getSenderUidPromise]).then(results => {
let instanceId = results[0].val();
let sender = results[1];
console.log('notifying ' + receiverUid + ' about ' + message.text + ' from ' + senderUid);
console.log('Sender ', sender);
var badgeCount = 1;
let payload = {
notification: {
uid: sender.uid,
title: 'New message from' + ' ' + sender.displayName,
body: message.text,
sound: 'default',
badge: badgeCount.toString()
},
'data': {
'notificationType': "messaging",
'uid': sender.uid
}
};
badgeCount++;
admin.messaging().sendToDevice(instanceId, payload)
.then(function (response) {
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
});
const getDeviceTokensPromise = event.data.child('token_To');
should be there instated of getting data from database reference.
or
with fixed path without wildcard like below
const getDeviceTokensPromise = admin.database().ref('/${chatroom}/${mid}/token_to').once('value');
where chatroom and mid is variable which contain value
Second thing:
if (!tokensSnapshot.exists()) {
should in replace of
if (!tokensSnapshot.hasChildren()) {
third thing:
I am not sure about push notification tokenId but
is it required to do?
const tokens = Object.keys(tokensSnapshot.val());
may be we can use directly like below to send push notification
const tokens = tokensSnapshot.val();
You could store all device tokens in a node called tokens like in my example. Tokens could be an array if you would like one user to be able to get notifications on multiple devices. Anyway, store them by their UID.
This works for both Andriod and iOS.
Here is my code:
function loadUsers() {
let dbRef = admin.database().ref('/tokens/' + recieveId);
console.log(recieveId)
let defer = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
let data = snap.val();
console.log("token: " + data.token)
//userToken = data.token
resolve(data.token);
}, (err) => {
reject(err);
});
});
return defer;
}
Next we create the notification. I created a lastMessage node to capture just the last message sent in the chat. It is just updated every time a new message is sent in a chat between two users. Makes it easy to get the value. Also makes it easy to show the message on the Conversations screen where there is a list of users who are in a conversation with the current user.
exports.newMessagePush =
functions.database.ref('/lastMessages/{rcId}/{sendId}').onWrite(event => {
if (!event.data.exists()) {
console.log("deleted message")
return;
}
recieveId = event.params.rcId
//let path = event.data.adminRef.toString();
// let recieveId = path.slice(53, 81);
return loadUsers().then(user => {
console.log("Event " + event.data.child("text").val());
let payload = {
notification: {
title: event.data.child("name").val(),
body: event.data.child("text").val(),
sound: 'default',
priority: "10",
}
};
return admin.messaging().sendToDevice(user , payload);
});
});
To implement this logic on your current data structure, just change this line:
let dbRef = admin.database().ref('/tokens/' + recieveId);
and this line:
exports.newMessagePush =
functions.database.ref('/lastMessages/{rcId}/{sendId}').onWrite(event
=> {
to your token location:
let dbRef =
admin.database().ref('/${chatroom}/${mid}/token_to');
and your conversation location:
exports.notifyMsg = functions.database.ref('/{chatroom}/{mid}/')
.onWrite(event => {
Then just change the notification payload be the message you want to display and throw in your error handling on the end of the sendToDevice function, as you did in your code.
Hopefully you figured all this out already but if not maybe this will help you or others trying to use Cloud Functions for notifications.
let payload = {
notification: {
uid: sender.uid,
title: 'New message from' + ' ' + sender.displayName,
body: message.text,
sound: 'default',
badge: badgeCount.toString()
},
'data': {
'notificationType': "messaging",
'uid': sender.uid
}
};
There are two types of FCMs.
1) Data
2) Notification
For detailed overview : FCM Reference
You have to fix your payload for both FCMS. And for Data FCM you have to extract Data in your FCM Service (Client) and generate a push notification according to your need.

Categories

Resources