Function in async function chain not being called - javascript

I'd like to update the user's data in Firestore whenever they log in and am using the following code to do so. For some reason, the code doesn't work (see comments) and doesn't create a custom User object from firebase.User. Why is this happening/how do I fix this? I'm not getting any errors.
Code that is called to log in
async emailLogIn(email: string, password: string) {
return this.auth.signInWithEmailAndPassword(email, password)
.then( async (credential) => {
this.analytics.logEvent('logged_in', { method: 'email' });
const firebaseUser = credential.user;
if(firebaseUser) {
const user = await this.createUserFromFirebaseUser(firebaseUser);
console.log(user); // This doesn't print anything
this.updateUserData(user);
if(!firebaseUser.emailVerified) {
this.sendEmailVerification();
}
}
});
}
Code that is convert firebase.User to User (doesn't work)
async createUserFromFirebaseUser(firebaseUser: firebase.User): Promise<User> {
console.log('createUserFromFirebaseUser()');
const currentUser = await this.user.toPromise();
console.log(currentUser); // This doesn't print anything
if(currentUser)
return currentUser;
const user: User = {
uid: firebaseUser.uid,
email: firebaseUser.email,
displayName: firebaseUser.displayName,
settings: {
language: 'English',
isPrivate: false,
newFountainNotification: true,
userFountainNotification: true,
feedbackNotification: true,
units: 'Metric'
}
}
return user;
}
Getting user data from Firestore
this.user = this.auth.authState.pipe(
takeUntil(this.destroy),
switchMap( (user) => {
if(user) {
return (this.firestore.collection('users').doc(user.uid).valueChanges() as Observable<User>)
} else {
return of(null);
}
})
);

It seemed to be an issue with using this.user.toPromise()
This is the code that works:
return this.user.pipe(
take(1),
map( (currentUser) => {
if(currentUser)
return currentUser;
const user: User = {
uid: firebaseUser.uid,
email: firebaseUser.email,
displayName: firebaseUser.displayName,
settings: {
language: 'English',
isPrivate: false,
newFountainNotification: true,
userFountainNotification: true,
feedbackNotification: true,
units: 'Metric'
}
}
return user;
})
).toPromise()

Related

Express doesn't get another user with .map()

I came to a problem, where I can create conversations with multiple people 2 and so on. However, I can't understand why it doesn't store data to seperate User models.
Here is a code that you only need to know:
router.post(
"/",
auth,
[
check("conversators", "There should be at least two conversators").isLength(
{ min: 2 }
),
],
async (req, res) => {
const { conversators } = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
let conversation = new Conversation({
user: req.user.id,
conversators: conversators,
});
await conversators.map(async (conversator) => {
let user = await User.findById(conversator);
let newData = user;
newData.conversations.push(conversation.id);
console.log('Created data', newData);
let newUser = await User.findOneAndUpdate(
{ user: conversator },
{
$set: {
newData,
},
},
{ new: true }
);
await newUser.save();
console.log(newUser);
});
await conversation.save();
res.status(200).json(conversation);
} catch (error) {
console.error(error.message);
res.status(500).send("Server error.");
}
}
);
module.exports = router;
What I can assure is that this line: console.log('Created data', newData); prints the desired data. However, the next console: console.log(newUser); prints the same User model as the previous one.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
surname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
conversations: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "conversation",
},
],
date: {
type: Date,
default: Date.now,
},
});
module.exports = User = mongoose.model("user", UserSchema);
The reason might be the difference in search methods used to get a record for newData and newUser. You have used User.findById for newData, which will obviously return different objects for different ids. But User.findOneAndUpdate uses filter criteria that may satisfy several results, but only first will be returned. So it boldly depends on what that user field is.
Here is the part that I changed and started to see the data on MongoDB:
await conversators.map(async (conversator) => {
let user = await User.findById(conversator);
let newData = user;
newData.conversations.push(conversation.id);
new Promise(async (resolve, reject) => {
user = await User.findOneAndUpdate(
{ id: conversator },
{
$set: {
newData,
},
},
{ new: true }
);
return resolve;
})
return await user.save();
});
Posted on behalf of the question asker

custom session next js next-auth

I got an problem when migrate my js file jo tsx, what I'm doing is signin with credentials and custom the session user to my user data
// api/auth/[...nextauth].js
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
import { ConnectDatabase } from "../../../lib/db";
import { VertifyPassword } from "../../../lib/password";
import { getSelectedUser } from "../../../helpers/database";
import { MongoClient } from "mongodb";
import { NextApiRequest } from "next";
interface credentialsData {
data: string | number;
password: string;
}
export default NextAuth({
session: {
jwt: true,
},
callbacks: {
async session(session) {
const data = await getSelectedUser(session.user.email);
session.user = data.userData;
// inside data.userdata is a object
// {
// _id: '60a92f328dc04f58207388d1',
// email: 'user#user.com',
// phone: '087864810221',
// point: 0,
// role: 'user',
// accountstatus: 'false'
// }
return Promise.resolve(session);
},
},
providers: [
Providers.Credentials({
async authorize(credentials: credentialsData, req: NextApiRequest) {
let client;
try {
client = await ConnectDatabase();
} catch (error) {
throw new Error("Failed connet to database.");
}
const checkEmail = await client
.db()
.collection("users")
.findOne({ email: credentials.data });
const checkPhone = await client
.db()
.collection("users")
.findOne({ phone: credentials.data });
let validData = {
password: "",
email: "",
};
if (!checkEmail && !checkPhone) {
client.close();
throw new Error("Email atau No HP tidak terdaftar.");
} else if (checkEmail) {
validData = checkEmail;
} else if (checkPhone) {
validData = checkPhone;
}
const checkPassword = await VertifyPassword(
credentials.password,
validData.password
);
if (!checkPassword) {
client.close();
throw new Error("Password Salah.");
}
client.close();
// inside validData is a object
// {
// _id: '60a92f328dc04f58207388d1',
// email: 'user#user.com',
// phone: '087864810221',
// point: 0,
// role: 'user',
// accountstatus: 'false'
// }
return validData;
},
}),
],
});
// as default provider just return session.user just return email,name, and image, but I want custom the session.user to user data what I got from dababase
This in client side
// index.tsx
export const getServerSideProps: GetServerSideProps<{
session: Session | null;
}> = async (context) => {
const session = await getSession({ req: context.req });
if (session) {
if (session.user?.role === "admin") {
return {
redirect: {
destination: "/admin/home",
permanent: false,
},
};
}
}
return {
props: {
session,
},
};
};
But in client side I got warning
Property 'role' does not exist on type '{ name?: string; email?: string; image?: string;
actually my file still working fine, but when my file in js format, its not warning like that
can someone help me to fix it ?
Not sure if you found a workaround yet but you need to configure the jwt callback as well! Here is an example from a project of mine:
callbacks: {
async session(session, token) {
session.accessToken = token.accessToken;
session.user = token.user;
return session;
},
async jwt(token, user, account, profile, isNewUser) {
if (user) {
token.accessToken = user._id;
token.user = user;
}
return token;
},
},
To explain things. jwt function always runs before session, so whatever data you pass to jwt token will be available on session function and you can do whatever you want with it. In jwt function i check if there is a user because this only returns data only when you login.
I imagine by now you have this solved, but since I ran across this page with the same issue I figured I'd post my solution. Just in case someone else runs across it. I'm new to typescript/nextjs and didn't realize I simply had to create a type definition file to add the role field to session.user
I created /types/next-auth.d.ts
import NextAuth from "next-auth";
declare module "next-auth" {
interface Session {
user: {
id: string;
username: string;
email: string;
role: string;
[key: string]: string;
};
}
}
Then I had to add this to my tsconfig.json
"include": ["next-env.d.ts", "types/**/*.ts", "**/*.ts", "**/*.tsx"],

Model.findById() is returning undefined

I trying to implement a favorite toggle where it saves the favorites in an array, I create a Schema and a router, the code you can see below the problem is when I try to test it on insomnia I'm getting undefined on my console.log(isFavorite). I don't know what could be wrong.
const userSchema = new Schema({
username: String,
email: String,
password: String,
favorites: [{ type: Schema.Types.ObjectId, ref: "Places" }],
},
{
timestamps: true,
});
// route
router.put("/favorites/:placeId", (req, res) => {
const userId = "5ebd13df31430045957db8c3";
User.findById(userId).then( (user) => {
const isFavorite = user.favorites.find( (favorite) => {
return favorite === req.params.placeId;
});
console.log(isFavorite);
console.log(req.params.placeId);
if (isFavorite) {
User.findOneAndUpdate(
{ _id: userId },
{
$pull: { favorites: req.params.placeId },
},
{
new: true,
})
.then((user) => res.json(user))
.catch((err) => res.status(400).json(err));
} else {
User.findOneAndUpdate(
{ _id: userId },
{
$push: { favorites: req.params.placeId },
},
{
new: true,
})
.then((user) => res.json(user))
.catch((err) => res.status(400).json(err));
}
});
});
this chunk is bad:
User.findById(userId).then((user) => {
const isFavorite = user.favorites.find((favorite) => {
return favorite === req.params.placeId;
});
instead must use populate():
let favorites = await User.findById(userId).populate('favorites');
and then filter favorites by placeId

mongodb : get all users

how do i edit this function of mine to get all users ? I have just started learning async await and i am having hard time learning how to get the request body.
here is my function :
export const get: Operation = async (
req: express.Request,
res: express.Response
) => {
commonUtility.showRequestParam(req);
let users: db.IUserDocument[] = [];
try {
// Describe data acquisition and registration from mongoDB here.
users = await UserModel.find()
.then(data => {
return data;
})
.catch(err => {
throw err;
});
} catch (err) {
// Error.
api.responseError(res, err);
}
if (users.length < 1) {
// this case is 404 ???
api.responseJSON(res, 200, []);
}
};
here is my user model:
export const usersSchema = new Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
BaseFields
});
export const UserModel = mongoose.model<db.IUserDocument>('Users', usersSchema);
You don't need to use .then when using async and await
export const get: Operation = async (
req: express.Request,
res: express.Response
) => {
commonUtility.showRequestParam(req);
let users: db.IUserDocument[] = [];
try {
users = await UserModel.find();
api.responseJSON(res, 200,users);
} catch (err) {
// Error.
api.responseError(res, err);
}
};
Read more about async await here -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Hapi.js user authentication with mongodb issue

For, an user verification now I hardcoded the username and password directly on my code. But I want this dynamically using database username and password. As, i'm new to hapi.js it seems quite difficult for me. This is my code :
app.js
const auth = require('hapi-auth-basic');
const hapi = require('hapi');
mongoose.connect('mongodb://localhost:27017/db', {
useNewUrlParser: true }, (err) => {
if (!err) { console.log('Succeeded.') }
else { console.log(`Error`)}
});
const StudentModel = mongoose.model('Student', {
username: String,
password: String
});
const user = {
name: 'jon',
password: '123'
};
const validate = async (request, username, password, h) => {
let isValid = username === user.name && password === user.password;
return {
isValid: isValid,
credentials: {
name: user.name
}
};
};
const init = async () => {
await server.register(auth);
server.auth.strategy('simple', 'basic', {validate});
server.auth.default('simple');
server.route({
method: 'GET',
path: '/',
handler: async (request, h) => {
return 'welcome';
}
});
}
I tried to do this by changing the validate as below :
const validate = async (request, username, password, h) => {
let isValid = username === request.payload.name && password === request.payload.password;
return {
isValid: isValid,
credentials: {
name: request.payload.name
}
};
};
but i got the type error "name" as it's natural. How can I modify this?
Here, fetch user and check in the validation method
const validate = async (request, username, password, h) => {
// fetch user here
const user = await StudentModel.findOne({username, password}).exec();
// user doesn't exist
if(!user) return {isValid: false}
// just make sure here user really exists
return {
isValid: true,
credentials: {
name: user.name
}
}
}

Categories