Updating data and new object relationship - sequelize - javascript

I have this function on my express route that update a user information and its role. The role is another Sequelize Object and I have set up a relationship as one to many:
User.belongsTo(Role);
Role.hasMany(User);
In my route this is my updatefunction:
const UpdateUser = async user => {
if (Object.keys(user).length !== 0) {
const { id, firstName, lastName, phone, email, role } = user;
let modUser = await User.findOne({ where: { id }, include: [{ model: Role}] });
if (modUser.firstName !== firstName) modUser.firstName = firstName;
if (modUser.lastName !== lastName) modUser.lastName = lastName;
if (modUser.phone !== phone) modUser.phone = phone;
if (modUser.email !== email && UniqueEmail(email)) modUser.email = email;
if(modUser.Role.id !== role) {
await modUser.setRole(await Role.findByPk(role));
}
modUser.save();
modUser = await User.findOne(
{
where: { id },
include: [{ model: Role}],
attributes: { exclude: ['RoleId', 'password'] }
},
);
return modUser.reload();
}
return { error: "No user found" };
}
The function works fine updating the info and the new role in the DB, the problem is that the returning User, sometimes doesn't have the updated info, but the previos info. I am not sure if I am implementing this wrong, or trying to update the User model and then asigning a new Role at the the time is breaking something.
Any ideas? Thanks!

Most methods in sequelize are async... I wasn't using my await keywords to let the asynchronos code resolve before moving on.
await modUser.reload();
await modUser.save();
Thanks to #Heiko TheiBen

Related

How to add an array of roles to member at once discord.js v13

I want to add the "Auto-Role System" to my discord bot.
I was doing well but it went into an error, You can check the end of the article for errors.
What I want to do is:
The owner uses the command by mentioning a role or a bunch of roles
Bot stores them in an array and then saves it on the database
When a user joined the guild, the bot gives that roles array to a member
So at first, we need to make a model for the database so I did create one:
// Guild.js
const mongoose = require('mongoose');
const guildConfigSchema = mongoose.Schema({
guildId: { type: String, match: /\d{18}/igm, required: true },
autoRoleDisabled: {
type: Boolean,
},
autoRoleRoles: {type: Array},
});
module.exports = mongoose.model('guild', guildConfigSchema);
Then I coded the setup command:
const role = message.mentions.roles.first();
if (!role) return message.channel.send('Please Mention the Role you want to add to other Auto Roles.');
Schema.findOne({ guildId: message.guild.id }, async (err, data) => {
if (data) {
data.autoRoleDisabled = false;
data.autoRoleRoles.push(role.id);
data.save();
} else {
new Schema({
guildId: message.guild.id,
autoRoleDisabled: false,
$push: { autoRoleRoles: role.id }
}).save();
}
message.channel.send('Role Added: ' + `<#&${role.id}>`);
})
In the end we need to make it work:
// Main.js
client.on("guildMemberAdd", async (member) => {
// ****Auto-Role****
const Welcome = require('./models/Guild');
try {
Welcome.findOne({ guildId: member.guild.id }, async (err, data) => {
if (!data) {
return;
} else {
if (data.autoRoleDisabled == false) {
let roles = data.autoRoleRoles;
roles.forEach(r => {
guildRrole = member.guild.roles.cache.find(role => role.id)
member.roles.add(guildRrole);
})
} else {
return;
}
}
});
} catch (e) {
console.log(e);
}
});
But it doesn't work and gives an error:
Error: cyclic dependency detected
at serializeObject (C:\Users\Pooyan\Desktop\PDMBot\node_modules\bson\lib\bson\parser\serializer.js:333:34)
And I think the problem is from pushing role IDs in the array.
Notes: I am using discord.js#13.8.0 and Node.js v16
You can simply change
guildRrole = member.guild.roles.cache.find(role => role.id)
member.roles.add(guildRrole);
to
guildRrole = member.guild.roles.cache.get(r);
if (!guildRrole) return;
member.roles.add(guildRrole);
because you are not finding role with id, you just did .find(role => role.id) which just gives role id so you have to check role id with your role id if you want to do with .find() like:
.find((role) => {
role.id === r
})

Handling complex query parameters Express.Js

I'm making REST APIS with Express.js
I have the following express route:
/api/customer
I added multiple query params to the route like this:
/api/customer?name=jake
/api/customer?country=america
/api/customer?name=jake&country=america
/api/customer?name=jake&limit=10
In my controllers I handle all of these with If and there are so many cases I feel that this method would not scale, is there a better way of handling this ?
This is the code for my controller, I'm using Sequelize to query the database:
async function getAllCustomer(queryLimit, page) {
const customers = await Customer.findAll({
limit: queryLimit ? parseInt(queryLimit) : null,
offset: page ? parseInt(queryLimit) * parseInt(page) : null
});
return customers;
}
async function getCustomerByFirstName(name, queryLimit, page) {
return await Customer.findAll({
where: {
firstName: name,
}
})
}
async function getCustomerByAddress(address) {
return await Customer.findAll({
where: {
customerAddress: address
}
})
}
async function getCustomerByNameAddress(name, address) {
return await Customer.findAll({
where: {
[Op.and]: [
{firstName: name},
{customerAddress: address}
]
}
})
}
async function getCustomer(req, res) {
const page = req.query.page;
const queryLimit = req.query.limit;
const name = req.query.name;
const address = req.query.address;
let customers;
/* User want to find first names */
if (name && !address) {
const names = name.split(",")
customers = await getCustomerByFirstName(names, queryLimit, page)
res.status(200).send(customers)
return;
}
/* User want to find addresses */
if (!name && address) {
const addresses = address.split(",")
customers = await getCustomerByAddress(addresses, queryLimit, page)
res.status(200).send(customers)
return;
}
/* User want to mix both */
if (name && address) {
const names = name.split(",")
const addresses = address.split(",")
customers = await getCustomerByNameAddress(names, addresses, queryLimit, page)
res.status(200).send(customers)
return;
}
if (!name && !address) {
customers = await getAllCustomer(queryLimit, page)
res.status(200).send(customers)
return;
}
}
You could do something like this:
async function getCustomer(req, res) {
const page = req.query.page;
const queryLimit = req.query.limit;
const name = req.query.name;
const address = req.query.address;
let query = { };
if(name) {
query.firstName = name;
}
if(address) {
query.address = address;
}
let customers = await getCustomers(query, queryLimit, page);
res.status(200).send(customers)
return;
}
async function getCustomers(query, queryLimit, page) {
const customers = await Customer.findAll({
where: query,
limit: queryLimit ? parseInt(queryLimit) : null,
offset: page ? parseInt(queryLimit) * parseInt(page) : null
});
return customers;
}
BTW, in your code, the functions getCustomerByFirstName, getCustomerByAddress and getCustomerByNameAddress are expecting to receive name and address as string parameter, but you are passing names and addresses array. This might lead to errors...

How to get specific property of an object?

So I'm sending data properly to mongo and data (user input information), which is correctly held in backend. In console I'm getting interceptor that tells me that data is received from Mongo DB, but how to properly get those properties (user's email, title of photo and url blob) or 'data'? So it can be seen as individual data (email, title...) and not as the whole object like it can be seen in console now.
--THIS IS IN MY VUE--
dohvatiObjavu(){
this.objava = Objave.dohvati_objavu();
console.log("Current post " + this.objava);
}
},
-- THIS IS IN SERVICES--
[let Objave = {
async dohvati_objavu() {
let response = await Service.get(/galerija)
let data = response.data;
console.log("Current posts in services: "+data.naslov)
return {
id: data._id,
email: data.email,
naslov: data.naslov,
noviOpisSlike: data.noviOpisSlike,
slika: data.slikaReference,
}
},
}
--THIS IS IN BACKEND--
app.get ('/galerija', async (req , res) => {
let db = await connect();
let cursor = await db.collection('galerija').find();
let results = await cursor.toArray();
res.json(results);
});
-- MY CONSOLE--
Objave.dohvati_objavu(); is an async function. So you should also await this inside your Vue method dohvatiObjavu().
I created a simplified working example, based on your code:
const Objave = {
dohvati_objavu: async function() {
// mock Service.get(/galerija) with Promise.resolve
const data = await Promise.resolve({
id: 'mockId',
email: 'mockEmail',
naslov: 'mockNaslov',
noviOpisSlike: 'mockNoviOpisSlike',
slika: 'mockSlika',
});
return {
id: data._id,
email: data.email,
naslov: data.naslov,
noviOpisSlike: data.noviOpisSlike,
slika: data.slikaReference
}
}
}
const MyVueComponent = class {
objava = undefined;
// DOES NOT WORK
dohvatiObjavu() {
this.objava = Objave.dohvati_objavu();
console.log("[dohvatiObjavu] Current post ", this.objava);
}
// WORKS
async dohvatiObjavu2() {
this.objava = await Objave.dohvati_objavu(); // <!-- await
console.log("[dohvatiObjavu2] Current post ", this.objava);
}
}
const component = new MyVueComponent()
component.dohvatiObjavu();
component.dohvatiObjavu2();

Valdaition for user exist or not using nodejs

I'm working on a project in React and ran into a problem that has me stumped.
I want to add new user but want to check if user is present or not I tried to give custom message but still not giving me my custom message.
so here is my code and i am not able to figure out how to give custom message.
if any one have some solution it will be really great if you help me
const Employee = require("../models/employeeSchema");
//custom imports
const AppError = require("../helpers/appErrorClass");
const sendErrorMessage = require("../helpers/sendError");
const sendResponse = require("../helpers/sendResponse");
const addEmployees = async (req, res, next) => {
try {
let data = req.body;
let newEmployee = {};
newEmployee.firstName = data.firstName.trim();
newEmployee.lastName = data.lastName.trim();
newEmployee.company = data.company.trim();
newEmployee.email = data.email.trim();
newEmployee.contact = data.contact;
newEmployee.skills = data.skills.split(",").map((skill) => {
return skill.trim();
});
newEmployee.altDescription = data.altDescription.trim();
newEmployee.hobbies = data.hobbies.split(",").map((hobby) => {
return hobby.trim();
});
newEmployee.socialLinks = [
{
gitHub: data.gitHub,
linkedIn: data.linkedIn,
website: data.website,
},
];
newEmployee.imageUrl = req.image;
let employee = await Employee.find({ email: newEmployee.email });
if (employee.email === newEmployee.email) {
return sendErrorMessage(
new AppError(
400,
"Unsuccessful",
"Email already exist try with another Email id"
),
req,
res
);
} else {
let data = await Employee.create(newEmployee);
sendResponse(200, "Employee added Succesfully", data, req, res);
}
} catch (err) {
return sendErrorMessage(new AppError(400, "unsuccessful", err), req, res);
}
};
module.exports.addEmployees = addEmployees;
I have no idea why this is happening, if anyone has experienced this I would be grateful.
Edit:
console.log(employee) outputs the following:
[{
employeeId: 'c88wgb4ocuw01609613746852',
contact: 123456789,
imageUrl: 'http://res.cloudinary.com/altafshaikh/image/upload/v1609613765/qbl7ujyz0wi6cy0xt3rf.jpg',
skills: [ 'sleep', 'laziness' ],
altDescription: 'Hello Saurav',
hobbies: [ 'Nautanki', 'doubtfull' ],
socialLinks: [ {} ],
_id: 5ff0c1c5327f300a55f6259a,
firstName: 'Saurav',
lastName: 'Upadhyay',
company: 'Raw',
email: 'sourav23#gmail.com',
__v: 0
}]
It's because employee is an array. You need to access the first element.
if (employee.length && employee[0].email === newEmployee.email) {
return sendErrorMessage(
new AppError(
400,
"Unsuccessful",
"Email already exist try with another Email id"
),
req,
res
);
}
Since the db query condition is email, we can assume that it will return empty array if no matches are found. It also means that the array will be populated only if a match is found. So we can also avoid the unnecessary check of emails being equal. You can do this.
if (employee.length) {
return sendErrorMessage(
new AppError(
400,
"Unsuccessful",
"Email already exist try with another Email id"
),
req,
res
);
}

MongoDB/Express: Why does Array.includes return false instead of true?

I'm working on an tiny app that allows user to participate in polls, but I'm having problems checking if the current user has already voted in the poll. Everything else works fine, save for the IIFE that checks for said condition, as seen in the code snippet included. Indeed, I'm getting false as opposed to true with the data I have i.e. I already seeded the DB with sample data, including a random poll that contains the array of IDs for users who've already voted. I tried testing one ID against said array, which returns false as opposed to the expected true. What gives?
Below are the relevant snippets.
Model
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
const ChoiceSchema = new Schema({
name: { type: String },
votes: { type: Number }
});
const PollSchema = new Schema({
title: { type: String },
category: { type: String },
choices: [ChoiceSchema],
addedBy: { type: Schema.Types.ObjectId, ref: 'User' },
votedBy: [{ type: Schema.Types.ObjectId, ref: 'User' }]
});
const Poll = mongoose.model('Poll', PollSchema);
export default Poll;
Controllers
import Poll from '../models/poll';
export default {
fetchAllPolls: async (req, res) => {
/*...*/
},
fetchSpecificPoll: async (req, res) => {
/*...*/
},
voteInPoll: async (req, res) => {
const { category, pollId } = req.params;
const { name, choiceId, voterId } = req.body;
try {
const poll = await Poll.findById(pollId);
const choice = await poll.choices.id(choiceId);
const votedChoice = {
name,
votes: choice.votes + 1,
};
// Check if user has already voted in poll
const hasVoted = ((votersIds, id) => votersIds.includes(id))(
poll.votedBy,
voterId
);
if (!voterId) {
res
.status(400)
.json({ message: 'Sorry, you must be logged in to vote' });
} else if (voterId && hasVoted) {
res.status(400).json({ message: 'Sorry, you can only vote once' });
} else {
await choice.set(votedChoice);
await poll.votedBy.push(voterId);
poll.save();
res.status(200).json({
message: 'Thank you for voting. Find other polls at: ',
poll,
});
}
} catch (error) {
res.status(404).json({ error: error.message });
}
},
createNewPoll: async (req, res) => {
/*...*/
},
};
I think you are trying to compare ObjectId with String representing the mongo id.
This should work:
const hasVoted = ((votersIds, id) => votersIds.findIndex((item) => item.toString() === id) !== -1)(
poll.votedBy,
voterId
);
or
const hasVoted = ((votersIds, id) => votersIds.findIndex((item) => item.equals(new ObjectId(id))) !== -1)(
poll.votedBy,
voterId
);
EDIT:
As #JasonCust suggested, a simpler form should be:
const hasVoted = poll.votedBy.some(voter => voter.equals(voterId));
It is more than likely that poll.votedBy is not an array of ID strings. If you are using it as a reference field then it is an array of BSON objects which would fail using includes because it uses the sameValueZero algorithm to compare values. If that is true then you could either convert all of the IDs to strings first or you could use find and the equals methods to find a match.
Update: showing actual code example
Also, some would provide an easier method for returning a boolean value.
const hasVoted = poll.votedBy.some((voter) => voter.equals(voterId));

Categories