When I update one value of a users object, all other values are deleted in database. What can I do about this? - javascript

I am working on a school project with a team of developers. We have created a page where the users have a profile and that profile can be updated. We have come across a bug when a user updates its information. If I update my city for example, then all other information about my user disappears. I have to update the other fields as well, otherwise the database will only store the city that I recently updated.
What I want is that the information I filled in when I created my user is saved, besides from the information I change when I update my user.
Before update: {"username":"bobox","email":"bobox#hotmail.com","password":"test234","gender":"Man","age":"17","city":"Jönköping","games":"Battlefield V","usernameDiscord":"bigbox","usernameSteam":"bigbox","usernameOrigin":"bobox","_id":"wTs8IyRmcA40f3VN"}
After update: {"username":"bobox","email":"bobox#hotmail.com","password":"test234","gender":"","age":"","city":"New York","games":"","usernameDiscord":"","usernameSteam":"","usernameOrigin":"","_id":"wTs8IyRmcA40f3VN"}
Here is the code. I am thankful for all help. If there is anything else you need to know, just let me know.
Updating a user:
script.js
async function updateUser(age, city, gender, games, discord, steam, origin) {
const id = localStorage.getItem("userId")
const response = await fetch('http://localhost:8080/users/' + id, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
age: age,
city: city,
gender: gender,
games: games,
usernameDiscord: discord,
usernameSteam: steam,
usernameOrigin: origin
})
})
console.log(response)
const data = await response.json()
console.log(data)
}
function updateUsersIndex() {
let updateProfileBtn = document.querySelector(".Profile-Right__Update")
console.log(updateProfileBtn)
updateProfileBtn.addEventListener("click", async(event) => {
console.log("hej")
event.preventDefault()
const age = document.querySelector(".Age__Input").value
const city = document.querySelector(".City__Input").value
const gender = document.querySelector(".Gender__Input").value
const discord = document.querySelector(".Discord__Input").value
const steam = document.querySelector(".Steam__Input").value
const origin = document.querySelector(".Origin__Input").value
const games = document.querySelector(".Profile-Right__Select-Game").value
const hidden = document.querySelector(".hidden")
const updateUsers = await updateUser(age, city, gender, games, discord, steam, origin)
})
}
updateUsersIndex()
let profileUpdateBackBtn = document.querySelector(".Profile-Right__Back")
profileUpdateBackBtn.addEventListener("click", async(event) => {
event.preventDefault()
let updateProfile = document.querySelector(".Update-Profile")
let profile = document.querySelector(".Profile__Wrappe")
profile.classList.toggle("Hidden")
updateProfile.classList.toggle("Hidden")
})
app.js:
app.patch('/users/:id', async(req, res) => {
const result = await collectionsNEDB.users.update({ _id: req.params.id }, {
$set: {
"age": req.body.age,
"city": req.body.city,
"gender": req.body.gender,
"games": req.body.games,
"usernameDiscord": req.body.usernameDiscord,
"usernameSteam": req.body.usernameSteam,
"usernameOrigin": req.body.usernameOrigin
}
})
console.log(req.params.id)
res.json(result)
})
Creating a user:
script.js
async function createUser(username, email, password, repeatPassword, games, usernameDiscord, usernameSteam, usernameOrigin) {
const response = await fetch('http://localhost:8080/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
email: email,
password: password,
repeatPassword,
games: games,
usernameDiscord: usernameDiscord,
usernameSteam: usernameSteam,
usernameOrigin: usernameOrigin
})
})
console.log(response)
const data = await response.json()
if (response.status == 200) {
console.log(data.message)
if (data.message == "SUCCESS") {
console.log("Great")
let Success = document.querySelector(".Success")
Success.innerHTML = "Användare skapad!"
alert("Användare skapad!")
}
} else {
const data = await response.json()
const p = document.querySelector("p")
p.innerHTML = ""
for (let i = 0; i < data.errors.length; i++) {
const error = data.errors[i]
console.log(data.errors)
switch (error) {
case "ERROR_USER_ALREADY_EXISTS":
const hidden = document.querySelector(".Error")
hidden.classList.toggle("Hidden")
hidden.innerHTML = "Användarnamnet existerar redan!"
break;
case "ERROR_EMAIL_ALREADY_EXISTS":
const hiddenEmail = document.querySelector(".Error__Email")
hiddenEmail.classList.toggle("Hidden__Email")
hiddenEmail.innerHTML = "E-mail existerar redan!"
break;
case "ERROR_PASSWORD_MISMATCH":
const hiddenPassword = document.querySelector(".Error__Password")
hiddenPassword.classList.toggle("Hidden__Password")
hiddenPassword.innerHTML = "Lösenordet matchar inte"
break;
}
}
}
}
function init() {
let form = document.querySelector("#Reg-Form-1")
form.addEventListener("submit", async(event) => {
event.preventDefault()
const username = form.querySelector(".username").value
const email = form.querySelector(".email").value
const password = form.querySelector(".password").value
const repeatPassword = form.querySelector(".repeat-password").value
const games = form.querySelector(".gejms").value
const usernameDiscord = form.querySelector(".usernameDiscord").value
const usernameSteam = form.querySelector(".usernameSteam").value
const usernameOrigin = form.querySelector(".usernameOrigin").value
const hidden = document.querySelector(".hidden")
const createUsers = await createUser(username, email, password, repeatPassword, games, usernameDiscord, usernameSteam, usernameOrigin)
})
}
init()
app.js:
app.post("/register", async(req, res) => {
let user, email
if (process.env.NODE_ENV == "development") {
user = await collectionsNEDB.users.find({ username: req.body.username })
email = await collectionsNEDB.users.find({ email: req.body.email })
} else {
dataUser = await Database.collections.users.find({ username: req.body.username })
dataEmail = await Database.collections.users.find({ email: req.body.email })
user = await dataUser.toArray()
email = await dataEmail.toArray()
}
let errors = []
if (req.body.password !== req.body.repeatPassword) {
errors.push("ERROR_PASSWORD_MISMATCH")
} else if (user == false) {
if (email == false) {
let newUser = {
username: req.body.username,
email: req.body.email,
password: req.body.password,
gender: "",
age: "",
city: "",
games: req.body.games,
usernameDiscord: req.body.usernameDiscord,
usernameSteam: req.body.usernameSteam,
usernameOrigin: req.body.usernameOrigin
}
if (process.env.NODE_ENV == "development") {
const result = await collectionsNEDB.users.insert(newUser)
res.status(200).json({ message: "SUCCESS" })
} else {
let db = await Database.connect()
let users = db.collection("users")
const result = await users.insert(newUser)
res.status(200).json({ message: "SUCCESS" })
console.log(result)
}
} else {
errors.push("ERROR_EMAIL_ALREADY_EXISTS")
}
} else {
errors.push("ERROR_USER_ALREADY_EXISTS")
}
if (errors.length > 0) {
res.status(400).json({ errors: errors })
}
})

Related

Mongoose findOne() method and array map() method

Today I'm a little bit confused... I'm trying to get a list of posts of logged user by _id numbers included in user mongo document. When I call promise inside map method - I'm still receiving Array of Promise{}. I'm looking for info about this behavior but I don't understand why it behaves like this. It seems coded like it should be (in promise with async and await). getListOfThoughts method is created for posts download.
Thank you in advance
export const addThought = async (req: Request, res: Response) => {
const { email, thoughtContent } = req.body;
const getListOfThoughts = async (user: IUser) => {
const result = user.posts.map(async (el) => {
return await ThoughtModel.findOne({ _id: el });
});
return result;
};
if (email) {
try {
const user = await UserModel.findOne({ eMail: email });
const newThought = await ThoughtModel.create({
textContent: thoughtContent,
author: {
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
userPic: user.pic,
},
});
await UserModel.updateOne(
{ eMail: email },
{ $push: { posts: newThought._id } }
);
const updatedUser = await UserModel.findOne({ eMail: email });
const newListOfThoughts = await getListOfThoughts(updatedUser);
console.log("new List", newListOfThoughts);
res.status(201).send({
message: "Thought added",
userData: newListOfThoughts,
});
} catch (error) {
console.log(error);
res.json(error.message);
}
}
};
mongoose javascript mongodb async promise
I tried:
export const addThought = async (req: Request, res: Response) => {
const { email, thoughtContent } = req.body;
const getListOfThoughts = async (user: IUser) => {
const result = user.posts.map(async (el) => {
const post = await ThoughtModel.findOne({ _id: el });
return post;
});
return result;
};
if (email) {
try {
const user = await UserModel.findOne({ eMail: email });
const newThought = await ThoughtModel.create({
textContent: thoughtContent,
author: {
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
userPic: user.pic,
},
});
await UserModel.updateOne(
{ eMail: email },
{ $push: { posts: newThought._id } }
);
const updatedUser = await UserModel.findOne({ eMail: email });
const newListOfThoughts = await getListOfThoughts(updatedUser);
res.status(201).send({
message: "Thought added",
userData: newListOfThoughts,
});
} catch (error) {
console.log(error);
res.json(error.message);
}
}
};

How can I store my JWT Token in localstorage?

userAction.js -> Frontend, Action
export const login = (userID, password) => async (dispatch) => {
try {
dispatch({ type: USER_LOGIN_REQUEST });
const url = "http://localhost:8080/authenticate/";
const config = {
auth: {
username: userID,
password,
},
};
const data = {};
const response = await axios.post(
url,
data,
config,
)
dispatch({ type: USER_LOGIN_SUCCESS, payload: config});
if (response.status === 200) {
// Login succeeded
const token = response.data.token;
console.log("TOKEN\n" + token);
config.token = response.data.token;
console.log(response.data.token);
}
localStorage.setItem("userInfo", JSON.stringify(config) );
}
My login function in REST Server :
exports.login = async (req,res) =>{
const b64auth = (req.headers.authorization || '').split(' ')[1] || '';
const [userID, password] = Buffer.from(b64auth, 'base64').toString().split(':');
const user = await User.findOne({ userID: userID });
if(!user) return res.status(400).send('User not found');
const validPass = await bcrypt.compare(password, user.password);
if(!validPass) return res.status(400).send('Incorrect Password');
//const token = generateToken(user.userID);
let payload = {
userID: user.userID
}
const token = generateToken(userID);
res.header('Authorization', 'Bearer ' + token).json(user);
return token;
}
I generate my token this way :
const generateToken = (_id) => {
console.log('Signing token for ID ', _id);
console.log('Secret key is ', process.env.JWT_KEY);
const token = jwt.sign({ _id}, process.env.JWT_KEY, {
expiresIn: "30d",
});
console.log('Signed token: ', token);
return token;
};
I try to store my token in my "userInfo" State .. but only username and password is displayed not token ..It works before .. but I don´t know why it´s not working anymore ^^ I´m completely at a loss
I hope someone sees the error
my Console gives me the detail:
TOKEN
undefined
You are expecting response.data to be an object. In that case update your API handler to return property token in an object:
exports.login = async (req,res) =>{
const b64auth = (req.headers.authorization || '').split(' ')[1] || '';
const [userID, password] = Buffer.from(b64auth, 'base64').toString().split(':');
const user = await User.findOne({ userID: userID });
if(!user) return res.status(400).send('User not found');
const validPass = await bcrypt.compare(password, user.password);
if(!validPass) return res.status(400).send('Incorrect Password');
//const token = generateToken(user.userID);
let payload = {
userID: user.userID
}
const token = generateToken(userID);
return res.header('Authorization', 'Bearer ' + token).json({ user, token });
}
Hopefully that helps!

NEXT JS req.body undefined

not sure what I'm doing wrong?
I'm getting undefined when trying to upload my form data to my supabase the data is coming to the API undefined but when I pass it to the function it prints what I want to send to the API in my submit handler.
export const Quote = () => {
const [formIsValid, setFormIsValid] = useState(false);
//----------------------------_FORM VALIDATION------------------------------------
const {
value: firstName,
inputBlurChangeHandler: firstNameBlur,
isValid: firstNameValid,
hasError: firstNameInputHasError,
valueChangeHandler: firstNameChangeHandler,
reset: resetFirstName,
} = useInput((value) => value.trim() !== "");
**hooks & useEffect removed to shorten question they are same as above but different names**
console.log(formIsValid, "FORM IS VALID");
const formSubmitHandler = async (event) => {
event.preventDefault();
//UNDEFINEDS
await fetch("api/dbhandler", {
method: "POST",
body: {
firstname: firstName,
secondname: secondName,
street: streetAddress,
phone: phoneNumber,
email: emailAddress,
postal: postalCode,
about: quoteDescription,
},
headers: {
"Content-Type": `text/plain`,
},
});
};
API is coming as undefined in req. body but if I console log in the submit handler values are being passed to the function not sure what I am doing wrong
import { supabase } from "../../utils/supabaseClient";
const supabaseApiHandler = async (req, res) => {
console.log(req.body.firstname);
if (req.method === "POST") {
const firstname = req.body.firstname;
const secondname = req.body.secondname;
const email = req.body.email;
const street = req.body.street;
const postal = req.body.postal;
const phone = req.body.phone;
const about = req.body.about;
const { data, error } = await supabase.from("quotes").insert([
{
firstname,
secondname,
email,
street,
postal,
phone,
about,
},
]);
}
res.status(200).json({ name: "test" });
};
export default supabaseApiHandler;
If you have the body parser disabled in the API route, req.body will be empty.
I accidentally left this code in without using another body parser.
export const config = {
api: {
bodyParser: false,
},
};

Trouble updating a resource

I'm trying to update a transaction, but instead its updating that one and at the same time deleting the other ones. My focus is to just update one transaction. Can someone check my backend logic please.
My Schema:
const mongoose = require('mongoose')
mongoose.Schema.Types.String.set('trim', true)
const transactionSchema = mongoose.Schema(
{
note: { type: String, required: true },
amount: { type: Number, default: 0 },
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
},
{
timestamps: true,
}
)
const WalletSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
name: {
type: String,
trim: true,
required: [true, 'Please enter a name'],
},
balance: {
type: Number,
default: 0,
},
transactions: [transactionSchema],
createdAt: {
type: Date,
default: Date.now,
},
})
module.exports = mongoose.model('Wallet', WalletSchema)
My backend controller logic:
exports.updateWalletTransactions = asyncHandler(async (req, res, next) => {
const { amount, note } = req.body
const foundWallet = await Wallet.findOne({ user: req.user.id })
foundWallet.transactions = foundWallet.transactions.filter(
(trans) => trans._id.toString() === req.params.id
)
if (foundWallet.transactions) {
foundWallet.transactions[0].amount =
amount || foundWallet.transactions[0].amount
foundWallet.transactions[0].note = note || foundWallet.transactions[0]
const updatedTransaction = await foundWallet.save()
return res.status(200).json(updatedTransaction)
} else {
return next(new ErrorResponse('Transaction not found', 404))
}
})
The problem is that you're using the filter method of Array that will only return the specified transaction.
the better approach is to write your code using the map method like this:
exports.updateWalletTransactions = asyncHandler(async (req, res, next) => {
const { amount, note } = req.body;
const foundWallet = await Wallet.findOne({ user: req.user.id });
if (!foundWallet) return next(new ErrorResponse("no wallet found", 404));
const transIndex = foundWallet.transactions.findIndex(
(trans) => trans._id.toString() === req.params.id
);
if (!transIndex) return next(new ErrorResponse("Transaction not found", 404));
foundWallet.transactions = foundWallet.transactions.map((trans) => {
if (trans._id.toString() === req.params.id) {
trans.amount = amount || trans.amount;
trans.note = note || trans.note;
}
return trans;
});
const updatedWallet = await foundWallet.save();
const updatedTransactions = updatedWallet.transactions;
return res.status(200).json(updatedTransactions);
});
or you can do it like this:
exports.updateWalletTransactions = asyncHandler(async (req, res, next) => {
const { amount, note } = req.body;
const foundWallet = await Wallet.findOne({ user: req.user.id });
if (!foundWallet) return next(new ErrorResponse("no wallet found", 404));
const transIndex = foundWallet.transactions.findIndex(
(trans) => trans._id.toString() === req.params.id
);
const trans = foundWallet.transactions[transIndex];
if (trans) {
trans.amount = amount || trans.amount;
trans.note = note || trans.note;
} else {
return next(new ErrorResponse("Transaction not found", 404));
}
const updatedWallet = await foundWallet.save();
const updatedTransactions = updatedWallet.transactions;
return res.status(200).json(updatedTransactions);
});
You are overwriting the transaction. Better to iterate over each transaction and update on matching transaction id.
exports.updateWalletTransactions = asyncHandler(async(req, res, next) => {
const {
amount,
note
} = req.body;
const foundWallet = await Wallet.findOne({
user: req.user.id
})
let transFound = false;
if (foundWallet) {
foundWallet.transactions.forEach(trans => {
if (trans._id.toString() === req.params.id) {
transFound = true;
trans.amount = amount || trans.amount
trans.note = note || trans.note
}
})
if(transFound){
const updatedTransaction = await foundWallet.save()
return res.status(200).json(updatedTransaction)
} else {
return next(new ErrorResponse('Transaction not found', 404))
}
} else {
return next(new ErrorResponse('User Id not found', 404))
}
})

Injecting dependecy in services in component based strucuture

I follow modular or component based strucutre. I found a sample repo.
https://github.com/sujeet-agrahari/node-express-clean-architecture
So, there is a main component.module.js files which is responsible for connecting all other pieces like controller, route, and services.
For controller, services are being injected using higher order functions. Now, controller are super easy to test, I can stub or mock services easily.
auth.module.js
const router = require('express').Router();
const {
makeExpressCallback,
makeValidatorCallback,
} = require('../../middlewares');
// validator
const AuthValidator = require('./auth.validator');
// service
const { doRegister, doLogin, doCheckUserExist } = require('./auth.service');
const { BadRequestError } = require('../../utils/api-errors');
// controller
const controller = require('./auth.controller');
const register = controller.register({ BadRequestError, doCheckUserExist, doRegister });
const login = controller.login({ doCheckUserExist, doLogin });
const AuthController = { register, login };
// routes
const routes = require('./auth.routes')({
router,
AuthController,
AuthValidator,
makeValidatorCallback,
makeExpressCallback,
});
module.exports = {
AuthController,
AuthService: {
doCheckUserExist,
doLogin,
doRegister,
},
AuthRoutes: routes,
};
auth.controller.js
const login = (doCheckUserExist, doLogin) => async (httpRequest) => {
const { username, password } = httpRequest.body;
const userData = await doCheckUserExist({ username });
const loginData = {
username,
role: userData.role_id,
passedPassword: password,
actualPassword: userData.password,
};
const loginResult = await doLogin(loginData);
return {
statusCode: 200,
body: {
success: true,
message: 'Successfully logged in!',
data: loginResult,
},
};
};
const register = ({ BadRequestError, doCheckUserExist, doRegister }) => async (httpRequest) => {
const { username, password } = httpRequest.body;
try {
await doCheckUserExist({ username });
} catch (error) {
// user doesn't exist
const registerResult = await doRegister({ username, password });
return {
statusCode: 200,
body: {
success: true,
message: 'Registered successfully!',
data: registerResult,
},
};
}
throw new BadRequestError('User already exist!');
};
module.exports = { register, login };
Things are fine with the controller, now the problem is with the services. I can't find any pattern to make them thin and clean.
auth.services.js
const {
JWT_ACCESS_TOKEN_SECRET,
ACCESS_TOKEN_EXPIRES_IN,
SIGN_OPTION,
} = require('config');
const bcrypt = require('bcryptjs');
const { User } = require('../../db');
const { generateJWT } = require('./jwt.service');
const { NotFoundError, BadRequestError } = require('../../utils/api-errors');
const doRegister = async ({ username, password }) => {
const user = await User.create({
username,
password,
role_id: 1, // assign role id here
});
// generate access token
const payload = {
username,
role: user.role_id,
};
const token = await generateJWT({
secretKey: JWT_ACCESS_TOKEN_SECRET,
payload,
signOption: {
...SIGN_OPTION,
expiresIn: ACCESS_TOKEN_EXPIRES_IN,
},
});
return {
access_token: token,
...payload,
};
};
const doLogin = async ({
username, userRole, passedPassword, actualPassword,
}) => {
const isValidPass = bcrypt.compareSync(passedPassword, actualPassword);
if (!isValidPass) throw new BadRequestError('Username or Password is invalid!');
// generate access token
const payload = {
username,
role: userRole,
};
const token = await generateJWT({
secretKey: JWT_ACCESS_TOKEN_SECRET,
payload,
signOption: {
...SIGN_OPTION,
expiresIn: ACCESS_TOKEN_EXPIRES_IN,
},
});
return {
access_token: token,
...payload,
};
};
const doCheckUserExist = async ({ username }) => {
const user = await User.findOne({
where: {
username,
},
});
if (!user) throw new NotFoundError('User not found!');
return user;
};
module.exports = { doRegister, doLogin, doCheckUserExist };
A lot is happening in the services, model imports, constants imports, and other utils.
Now services become really hard to test.
Is there any way or pattern I can separate some logic from services and make them lighter?
I can implement reository pattern for db methods, but I am not aware how I can implement using sequelize?
Should I use also higher order function to inject all the utils and constants in the service like I did for controller?

Categories

Resources