Find User in Mongo DB and then update the field - node.js

I'm building an authentication link which is sent by the company to their team member to allow them access to join.
Currently, my logic is looking something like this - with a bit of semi-code.
Firstly, I have two schema's, one for company and one for user.
const CompanyProfileSchema = new mongoose.Schema(
{
companyTitle:{type:String, required: true, unique: true, default: ""},
companyPassword:{type:String, required: true, default: ""},
centralEmail:{type:String, required: true, unique: true, default: ""},
description:{type:String, required: true, default: ""},
inviteToken:{type:String, required: true, default:""},
industry:{type:String, required: true},
employees:{type: Number},
companyImage: {type:String, required: false, unique: false, default: ""},
locationCity:{type:String, required: false, unique: false, default: ""},
industry:{type:String, required: false, unique: false, default: ""},
country:{type:String, required: false, unique: false, default: ""},
teamMembers: {
type: String, required: true, default: 0
},
teamMemberDetails: {
memberName: [String], default: [],
memberEmail: [String], default: [],
memberRole: [String], default: []
},
highlights: {type: Array},
isAdmin:{
type: Boolean,
default: false,
},
isVerified:{
type: Boolean,
default: false,
},
accountLevel: {type: Array},
emailAuthorised: { type: Boolean, default: false},
invitedUsers: {type: Array}
},
);
module.exports = mongoose.model("CompanyUser", CompanyProfileSchema)
const UserSchema = new mongoose.Schema(
{
//userdetails
username: {type: String, required: true, default: "" },
password:{type:String, required: true},
//profile
fullname:{type:String, required: false, default: "none"},
firstname:{type:String, required: false, default: "", unique: false},
surname:{type:String, required: false, default: "", unique: false},
email:{type:String, required: true, unique: true},
position: {type:String, required: false, unique: false},
recruiter: {type: Boolean}, //Canidate or Recruiter,
userImage: {type:String, required: false, unique: false, default: ""},
//location
locationCity:{type:String, required: false, unique: false, default: ""},
country:{type:String, required: false, unique: false, default: ""},
//work
role:{type:String, required: false, default: ""},
company: {type:String, default: ""},
emailAuthorised: { type: Boolean, default: false},
professionalBio: {type:String, default: ""},
positionRecentTitle : {type:String, default: ""},
positionRecentCompany : {type:String, default: ""},
companyAuthorised: {type: String, default: ""},
isAdmin:{
type: Boolean,
default: false,
},
},
{timestamps: true }
);
module.exports = mongoose.model("User", UserSchema)
I have create an API endpoint which sends out an email with the auth token of the company.
I would like to firstly find the user in the DB and update the field 'companyAuthorised' with the userToken of the company.
Secondly, I'd also like to find the company with the token also and update their teamMemberDetails.Email to be the email in the endpoint.
I have created some psuedo code which I think is pretty close, can anyone clarify?
router.get('/inviteToJoinTeam/:token/:email', async (req, res) => {
try {
//verify the token against DB
const userToken = (req.params.token)
const indivEmailAdd = (req.params.email)
// creating user auth
try{
const userDetails = await User.find({email: indivEmailAdd})
const { password, ...others } = userDetails._doc;
const userID = userDetails.id
const updateUser = await User.findByIdAndUpdate(userID, { companyAuthorised: {userToken}});
}catch{
console.log('failure')
}
// creating company auth
try{
const companyDetails = await CompanyUser.find({inviteToken: userToken})
const { password, ...others } = companyDetails._doc;
const companyID = companyDetails.id
const updateCompany = await CompanyUser.findByIdAndUpdate(companyID, { teamMemberDetails.memberEmail: {indivEmailAdd}});
}catch{
console.log('failure')
}
console.log('successfully completed try')
res.send('final')
} catch (e) {
res.send('This isnt working');
}
});
Regards

If I correctly understand your use case, the code you are looking to write would be the following:
router.get('/inviteToJoinTeam/:token/:email', async (req, res) => {
try {
//verify the token against DB
const userToken = req.params.token;
const indivEmailAdd = req.params.email;
// creating user auth
try {
const updatedUser = await User.findOneAndUpdate(
{ email: indivEmailAdd },
{ companyAuthorised: userToken },
).lean();
} catch {
console.log('failure');
}
// creating company auth
try {
const updatedCompany = await CompanyUser.findOneAndUpdate(
{ inviteToken: userToken },
{
'teamMemberDetails.memberEmail': indivEmailAdd,
},
).lean();
} catch {
console.log('failure');
}
console.log('successfully completed try');
res.send('final');
} catch (e) {
res.send('This isnt working');
}
});

Related

Unable to Get Data from Secondary Collection MERN

I have a user Full stack tool, and i've separated the users into 'users' and 'companyusers' respectively within my backend.
Within this, I have a fully fuctioning GET for users, which looks like this:
router.get("/findUser/:id", async (req,res)=>{
try{
const user = await User.findById(req.params.id)
const { password, ...others } = user._doc;
res.status(200).json(others);
}catch(err){
res.status(500).json(err)
}
})
Pretty generic, but it works.
I've created the schema:
const mongoose = require("mongoose")
//CompanyProfileSchema
const CompanyProfileSchema = new mongoose.Schema(
{
companyTitle:{type:String, required: true, unique: true, default: ""},
companyPassword:{type:String, required: true, default: ""},
centralEmail:{type:String, required: true, unique: true, default: ""},
description:{type:String, required: true, default: ""},
industry:{type:String, required: true},
employees:{type: Number},
companyImage: {type:String, required: false, unique: false, default: ""},
locationCity:{type:String, required: false, unique: false, default: ""},
industry:{type:String, required: false, unique: false, default: ""},
country:{type:String, required: false, unique: false, default: ""},
highlights: {type: Array},
isAdmin:{
type: Boolean,
default: false,
},
isVerified:{
type: Boolean,
default: false,
},
invitedUsers: {type: Array}
},
);
module.exports = mongoose.model("CompanyUser", CompanyProfileSchema)
Which works and is in the index.js as such
app.use("/api/users", userRoute)
app.use("/api/companyprofile", companyprofileRoute)
However, in my companyprofile request file, when I complete the request, I am getting empty data back:
const CompanyProfile = require("../models/CompanyProfile");
const { route } = require("./auth");
router.get("/findCompany/:id", async (req,res)=>{
try{
const companyUser = await CompanyProfile.findById(req.params.id)
res.status(200).json(req.params.body);
const { password, ...others } = companyUser._doc;
// res.status(200).json({others});
}catch(err){
res.status(500).json(err)
}
})
This is with a request like:
localhost:5000/api/companyprofile/findCompany/61f2980a7d4bb550edf8ec7e
and a body of
{
"centralEmail": "natia1#bank.com.au"
}
Any ideas?

MongoDB mongoose filter with gender where user in profile is objectID

I am new in backend development.
I am trying to create profile endpoints to get all profiles with the gender of the user is either male or female.
Profile contains user as a objectID.
I want to filter profiles using users gender.
My UserSchema looks like this
const userSchema = mongoose.Schema(
{
firstname: {
type: String,
required: true,
trim: true,
},
lastname: {
type: String,
required: true,
trim: true,
},
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Invalid email');
}
},
},
password: {
type: String,
required: true,
trim: true,
minlength: 8,
validate(value) {
if (!value.match(/\d/) || !value.match(/[a-zA-Z]/)) {
throw new Error('Password must contain at least one letter and one number');
}
},
private: true, // used by the toJSON plugin
},
role: {
type: String,
enum: roles,
default: 'user',
},
gender: {
type: String,
enum: genders,
required: true,
},
profileStatus: {
type: String,
enum: profileStatus,
default: 'inProgress',
},
isEmailVerified: {
type: Boolean,
default: false,
},
},
{
timestamps: true,
}
);
And Profile schema looks like this
const profileSchema = new Schema(
{
user: { type: Schema.Types.ObjectId, ref: 'User', required: true, unique: true },
bio: { type: String, required: true },
profilePicUrl: {
type: String,
required: true,
},
birthdate: {
type: Date,
required: true,
},
profession: {
type: String,
required: true,
},
profileCompletion: {
type: Number,
default: 50,
min: 0,
max: 100,
},
credits: {
type: Number,
default: 2,
min: 0,
},
lastLogin: {
type: Date,
default: new Date(),
required: true,
},
},
{ timestamps: true }
);
I want to find profiles where user gender is male or female.
How can I do that?
You can create a endpoint like this and use .find() to find all the users with the gender passed by user
router.get(/user/:gender, async (req, res) => {
try {
const users = await User.find({ gender: req.params.gender }).exec();
res.status(200).json(users);
} catch (err) {
return res.status(500);
}
})

Sub-schema properties are not updated mongoose

little bit stuck with one issue which is related to mongoose. I have a Project schema with sub-schema(SingleUserSchema). Whenever I add a new User to this schema it saves everything and that's ok. The issue is that if I update the user, old values in schema does not update. Any ideas how to solve this case? Stuck here for a whole day already.
Schema:
const mongoose = require('mongoose');
const SingleUserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
role: {
type: String,
required: true,
},
status: {
type: String,
required: true,
},
});
const ProjectSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, 'Provide project name'],
minlength: 5,
},
description: {
type: String,
required: [true, 'Provide description about the project'],
},
maxWorkingEmployees: {
type: Number,
required: [
true,
'Provide maximum number of employees working on this project',
],
},
currentlyWorkingEmployees: [SingleUserSchema],
status: {
type: String,
enum: ['Pending', 'In progress', 'Paused', 'Delayed', 'Completed'],
default: 'Pending',
},
createdBy: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
},
{ timestamps: true }
);
module.exports = mongoose.model('Project', ProjectSchema);
Controller:
const attachEmployeeToProject = async (req, res) => {
const { projectId, userId } = req.params;
const project = await Project.findOne({ _id: projectId });
const user = await User.findOne({ _id: userId });
if (!user) {
throw new NotFoundError(`User with id ${userId} does not exists`);
}
if (!project) {
throw new NotFoundError(`Project with id ${userId} does not exists`);
}
const { role, email, status } = user;
const SingleUserSchema = {
email,
role,
status,
};
let currentlyWorkingEmployees = [
...project.currentlyWorkingEmployees,
SingleUserSchema,
];
req.body.currentlyWorkingEmployees = currentlyWorkingEmployees;
const updateProject = await Project.findOneAndUpdate(
{ _id: projectId },
req.body,
{
new: true,
runValidators: true,
}
);
res.status(StatusCodes.OK).json({ updateProject });
};
Maybe I just simply need to create a reference? Tried like this and received tons of errors, also felt lost how to create n references in array.
currentlyWorkingEmployees: [
{ type: mongoose.Schema.ObjectId, ref: 'User', required: true },
],
User schema:
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: [true, 'Please provide username'],
minlength: 3,
maxlength: 50,
},
email: {
type: String,
required: [true, 'Please provide username'],
unique: true,
validate: {
validator: validator.isEmail,
message: 'Please provide valid email address',
},
},
password: {
type: String,
required: [true, 'Please provide password'],
validator: {
validate: {
validator: validator.isStrongPassword,
message: 'Please provide stronger password',
},
},
},
firstname: {
type: String,
required: [true, 'Please provide first name'],
},
lastname: {
type: String,
required: [true, 'Please provide last name'],
},
status: {
type: String,
enum: ['Working', 'Pause', 'Offline'],
default: 'Offline',
},
role: {
type: String,
enum: [
'Developer',
'Analyst',
'Designer',
'Architect',
'Project Manager',
'Owner',
'Teamleader',
'Employee',
],
default: 'Employee',
},
verificationToken: {
type: String,
},
isTokenVerified: {
type: Boolean,
default: false,
},
tokenValidationDate: {
type: Date,
default: null,
},
});
First, as far as I see you don't have a model called 'User' so that's the reason why your reference is not working.
currentlyWorkingEmployees: [
{ type: mongoose.Schema.ObjectId, ref: 'User', required: true },
]
https://mongoosejs.com/docs/populate.html
Second, you need to identify the user you want to update (based on userId) inside currentlyWorkingEmployees collection, if I understood correctly your problem.
Hope it helps!

How can I get a field alone from a model to show

I have 2 models, a user model, and a follow model that handles the following of users. I would like to be able to get the followers to users in a get request. How can I get this?
Below is the user model
const userSchema = new mongoose.Schema({
fullname: {
type: String,
required: true,
trim: true,
lowercase: true
},
username: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true
},
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
password: {
type: String,
required: true,
minlength: 7,
trim: true,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error('Passwoed cannot contain "password"')
}
}
},
isActive: {
type: Boolean,
required: false,
default: true
}
})
Here is the Follow model that handles the following of users. When a logged-in user follows another user, we set the followedBy to the logged-in user and the user to the user that is getting followed.
const followSchema = new mongoose.Schema({
// the logged in user who will be trying to follow someone will be added to "followedBy"
// the user who is getting followed will be added to "user"
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
followedBy: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Showcase'
}
}, {
timestamps: true
})
Below is the post request where I would like to get a list of object id's that follows a user (passed through the id in the parameter)
// Get a users followers
router.get('/api/:id/followers', async (req, res) => {
try {
const followers = await Follow.find({
user: req.params.id
})
console.log(followers.followedBy)
res.status(200).send(followers)
} catch (e) {
res.status(500).send()
}
})
How can I get the object id's of followedBy?
You could put the followers and following actions into the user schema as attributes
const userSchema = new mongoose.Schema({
fullname: {
type: String,
required: true,
trim: true,
lowercase: true
},
username: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true
},
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
password: {
type: String,
required: true,
minlength: 7,
trim: true,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error('Passwoed cannot contain "password"')
}
}
},
isActive: {
type: Boolean,
required: false,
default: true
},
following: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
followers: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
]
});
const User = mongoose.model('User', userSchema)
if you still want to stick to having a Follow model here's a solution you might like
const followSchema = new mongoose.Schema({
//user below is the user that is being followed
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},{
timestamps: true
});
const Follow = mongoose.model('Follow', followSchema);
const userSchema = new mongoose.Schema({
fullname: {
type: String,
required: true,
trim: true,
lowercase: true
},
username: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true
},
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
password: {
type: String,
required: true,
minlength: 7,
trim: true,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error('Passwoed cannot contain "password"')
}
}
},
isActive: {
type: Boolean,
required: false,
default: true
},
following: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Follow",
},
],
followers: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Follow",
},
]
});
const User = mongoose.model('User', userSchema);
we can have a follow route in our API or server:
app.post('/follow/:user_id', (req, res) => {
//perform follow code
})
When user A clicks on a button to follow user B, we pass in user B's user_id as a parameter to our API like this
http://localhost:54757/follow/userB_id
We already know we're in user A's account because he's the one clicking on the follow button, so we can just get user A's user Id using an auth token that we must have given him upon signup/signin
In our API we create 2 new objects from the Follow model using the follow route
app.post('/follow/:user_id', (req, res) => {
//push this one into user B's "followers" array and save user B
const new_follow1 = new Follow({
user: userA_id //id gotten from session or auth token
});
//push this one into user A's "following" array and save user A
const new_follow2 = new Follow({
user: req.params.userB_id //id gotten from request parameters req.params
});
})
then finally save the two new Follows
new_follow1.save();
new_follow2.save()

How to delete the referenced document in one collection and its record from the referred other collection

In my NodeJS API and MongoDB, I'm trying to delete a record which is a reference to another collection.
What I would like to do is to delete the referred objectId and the records related to the other collection which is referred.
I have 2 models Profiles and Posts and I want to delete the same one post from Profile and Post collection.
I was able to delete the reference id in Profile but I don't know how to delete also the record from Posts collection.
I tried this:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
err => {
if (err) {
throw new ErrorHandlers.ErrorHandler(500, err);
}
res.json({ Message: "Deleted" });
}
);
} catch (error) {
res.status(500).send(error);
}
}
And my 2 models:
// Here defining profile model
// Embedded we have the Experience as []
const { Connect } = require("../db");
const { isEmail } = require("validator");
const postSchema = {
type: Connect.Schema.Types.ObjectId,
ref: "Post"
};
const experienceSchema = {
role: {
type: String,
required: true
},
company: {
type: String,
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: false
},
description: {
type: String,
required: false
},
area: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
username: {
type: String,
required: false
},
image: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
}
};
const profileSchema = {
firstname: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: [true, "Email is required"],
validate: {
validator: string => isEmail(string),
message: "Provided email is invalid"
}
},
bio: {
type: String,
required: true
},
title: {
type: String,
required: true
},
area: {
type: String,
required: true
},
imageUrl: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
},
username: {
type: String,
required: true,
unique: true
},
experience: [experienceSchema],
posts: [postSchema],
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
}
};
const collectionName = "profile";
const profileSchemaModel = Connect.Schema(profileSchema);
const Profile = Connect.model(collectionName, profileSchemaModel);
module.exports = Profile;
const { Connect } = require("../db");
const reactionSchema = {
likedBy: {
type: String,
unique: true,
sparse: true
}
};
const postSchema = {
text: {
type: String,
required: true,
unique: true,
sparse: false
},
profile: {
type: Connect.Schema.Types.ObjectId,
ref: "Profile",
},
image: {
type: String,
default: "https://via.placeholder.com/150",
required: false
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
reactions: [reactionSchema],
comments: {
type: Connect.Schema.Types.ObjectId,
ref: "Comment",
required: false
}
};
const collectionName = "post";
const postSchemaModel = Connect.Schema(postSchema);
const Post = Connect.model(collectionName, postSchemaModel);
module.exports = Post;
Just add a query to remove the post after pulling it's ID from the profile collection:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
// You don't need an error callback here since you are
// using async/await. Handle the error in the catch block.
);
await Posts.remove({ _id: req.params.postId });
} catch (error) {
// This is where you handle the error
res.status(500).send(error);
}
}

Resources