How can I fix double running function in firebase and react project? - javascript

I am trying to check existing of data on firebase real-time database and then add new data.
When I add exist data, it works well.
When I add new data, it works two time and don't add new data.
registerStaff = async (model) => {
if (!firebase.apps.length) {
return false;
}
if (model) {
return new Promise((resolve, reject) => {
this.db.ref("tbl_phone_number").on("value", async (snapshot) => {
if (snapshot.exists()) {
const samePhone = await _.filter(snapshot.val(), (o) => {
`find same phone number`;
return o.phone.toString() === model.phone.toString();
});
console.log("checking...", snapshot.val());
if (samePhone.length > 0) {
`checking samephone Number`;
console.log("exist...");
`if exist, return error`;
resolve({
type: "phone",
message: "The phone number is already used.",
});
} else {
`If there is no, add new phone number`;
const newPostKey = this.db
.ref()
.child("tbl_phone_number")
.push().key;
this.db
.ref(`tbl_phone_number/${newPostKey}`)
.set({ phone: model.phone, type: model.type })
.then(() => {
console.log("making...==>");
resolve({
type: "success",
message: "Successfully registered.",
});
})
.catch((err) => {
resolve({
type: "phone",
message: "Sorry. Something went wrong",
});
});
}
}
});
});
}
};
//console log result checking... checking... exist... checking... exist... making...

I found solution to solve this problem in using function "on" and "once".
'on' function of firebase is used to keep data from firebase and add new data automatically when I add new data.
'once' function of firebase is used to change data only once.
Therefore in above question, if I use 'once' function instead of 'on', it will work well.
Good luck.

Related

Catching Error with Nested Promise confusion

I have been working on my first node.js backend and I am trying to refactor the login function, but i am not understanding something about promise chaining and error handling. I am hoping someone can help point out what I am missing here.
I have read about promise chaining and I understand that one catch should be able to handle any error in the chain, but I cant seem to get it to work without having an individual .catch for every .then
Here is my userLogin function.
const loginUser = handleAsync(async (req, res, next) => {
let userEmail = req.body.email;
let submittedPassword = req.body.password;
userLogin(userEmail, submittedPassword)
.then((userObject) => {
res.json(userObject);
})
.catch((err) => {
res.status(401).send(err);
console.log("any error inside userLogin: " + err);
});
});
async function userLogin(email, submittedPassword) {
console.log("in user login");
return new Promise(function (resolve, reject) {
getUserAccountByEmail(email)
.then((userAccountResults) => {
let email = userAccountResults[0].email;
let storedPassword = userAccountResults[0].password;
//the user exists check the password
//the user exists. Now Check password.
checkUserPassword(submittedPassword, storedPassword)
.then((passwordIsAMatch) => {
//The password is a match. Now Generate JWT Token.
generateJWTToken(email)
.then((tokens) => {
//build json object with user information and tokens.
createUserDataObject(tokens, userAccountResults)
.then((userObject) => {
//send the user object to the front end.
resolve(userObject);
})
.catch((err) => {
reject(err);
});
})
.catch((err) => {
reject(err);
});
})
.catch((err) => {
reject(err);
});
})
.catch((err) => {
reject(err);
});
});
}
And here is one of the functions that is a part of the chain
function getUserAccountByEmail(email) {
return new Promise(function (resolve, reject) {
logger.info("Looking up user email");
const users = User.findAll({
where: {
email: email,
},
limit: 1,
attributes: [
"email",
"password",
"userId",
"stripeConnectAccountId",
"isStripeAccountSet",
"stripeCustomerId",
],
})
.then((userResults) => {
if (doesUserExist(userResults)) {
resolve(userResults);
} else {
console.log("user doesnt exist in getuseraccount");
reject("User Email Does Not Exist In Database");
}
})
.catch((error) => {
console.log("Error Accessing Database: UserAccountByEmail");
logger.error("Error in getUserAccountByEmail: " + error);
});
});
}
Any help would be appreciated. Thanks
I took jfriend00 's advice and refactored using await instead of nesting.
async function userLogin(email, submittedPassword) {
return new Promise(async function (resolve, reject) {
try {
//Get User Account Information
const userAccountResults = await getUserAccountByEmail(email);
//Password Authentication
let storedPassword = userAccountResults[0].password;
const passwordIsAMatch = await checkUserPassword(
submittedPassword,
storedPassword
);
//Generate JWT Tokens
const tokens = await generateJWTToken(email);
//Prepare user data JSON for sending to the frontend.
const userData = await createUserDataObject(tokens, userAccountResults);
resolve(userData);
} catch (error) {
reject(error);
}
});
}

Firebase: can't write Username + Title to DB [duplicate]

So I'm following a Savvy Apps tutorial in order to learn Vue.js. This tutorial uses Firebase with Firestore. Since Firestore is in Beta (as the tutorial says), changes might happen - and I think that might be the case here.
In any case, I'm trying to sign up a new user. I fill out the form and click 'Sign up' and I get this error message:
Error: Function CollectionReference.doc() requires its first argument to be of type string, but it was: undefined
But looking in Firebase, I see that the user has been created. So why do I get this error message? What is the first argument?
The code for signup looks like this:
signup() {
this.performingRequest = true;
fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password).then(user => {
this.$store.commit('setCurrentUser', user);
// create user obj
fb.usersCollection.doc(user.uid).set({
name: this.signupForm.name,
title: this.signupForm.title
}).then(() => {
this.$store.dispatch('fetchUserProfile');
this.performingRequest = false;
this.$router.push('/dashboard')
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
},
Let me know if you need more code - this is the first time I'm testing Vue.js.
createUserWithEmailAndPassword() returns a Promise containing a UserCredential. UserCredential has a property user for the firebase.User object.
You need to make the appropriate changes to your code to correctly access the UID:
signup() {
this.performingRequest = true;
fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password)
.then(credential=> { // CHANGED
this.$store.commit('setCurrentUser', credential.user); // CHANGED
// create user obj
fb.usersCollection.doc(credential.user.uid).set({ //CHANGED
name: this.signupForm.name,
title: this.signupForm.title
}).then(() => {
this.$store.dispatch('fetchUserProfile');
this.performingRequest = false;
this.$router.push('/dashboard')
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
},

Turbo, adding params to post or other types of Turbo data not working

I am using Turbo which you can find more information about it here: https://www.turbo360.co/docs
What I am trying to do is to attach a parameter to a Post before it is created. In this case I am trying to attach a profile. I am not getting any errors and from what I see the param is going through just fine, but when I log out the post the profile param is not there.
Here is creating the post:
createPost(params) {
const { currentUser } = this.props.user;
if (currentUser == null) {
swal({
title: 'Oops...',
text: 'Please Login or Register before posting',
type: 'error'
});
return;
}
params['profile'] = currentUser;
console.log(params);
this.props
.createPost(params)
.then(data => {
swal({
title: 'Post Created',
text: `Title: ${data.title}`,
type: 'success'
});
})
.catch(err => console.log(err));
}
Here is the action createPost:
createPost: params => {
return dispatch => {
return dispatch(TurboClient.createPost(params, constants.POST_CREATED));
};
},
Here is the TurboClient function createPost:
const postRequest = (resource, params, actionType) => {
return dispatch =>
turbo({ site_id: APP_ID })
.create(resource, params)
.then(data => {
if (actionType != null) {
dispatch({
type: actionType,
data: data
});
}
return data;
})
.catch(err => {
throw err;
});
};
const createPost = (params, actionType) => {
return postRequest('post', params, actionType);
};
Now from here you can see where I log the params, this returns:
Here is what the post looks like once it is created:
It looks like you're trying to create a Post object. In your createPost method you return:
postRequest('post', params, actionType);
By using the word 'post' here you are creating it as a Post object, which has a very specific schema that it follows. If you would like to change that, you could try creating a Custom Object by doing something like this, for example:
postRequest('randomName', params, actionType);
Hope that helps.

How to validate Sequelize transaction and make it look nice

I am trying to re-learn NodeJS after a couple years of putting it down, so I'm building a small banking website as a test. I decided to use Sequelize for my ORM, but I'm having a bit of trouble sending money between people in a way that I like.
Here was my first attempt:
// myUsername - who to take the money from
// sendUsername - who to send the money to
// money - amount of money to be sent from `myUsername`->`sendUsername`
// Transaction is created to keep a log of banking transactions for record-keeping.
module.exports = (myUsername, sendUsername, money, done) => {
// Create transaction so that errors will roll back
connection.transaction(t => {
return Promise.all([
User.increment('balance', {
by: money,
where: { username: myUsername },
transaction: t
}),
User.increment('balance', {
by: -money,
where: { username: sendUsername },
transaction: t
}),
Transaction.create({
fromUser: myUsername,
toUser: sendUsername,
value: money
}, { transaction: t })
]);
}).then(result => {
return done(null);
}).catch(err => {
return done(err);
});
};
This worked, but it didn't validate the model when it was incremented. I'd like for the transaction to fail when the model does not validate. My next attempt was to go to callbacks, shown here (same function header):
connection.transaction(t => {
// Find the user to take money from
return User
.findOne({ where: { username: myUsername } }, { transaction: t }) .then(myUser => {
// Decrement money
return myUser
.decrement('balance', { by: money, transaction: t })
.then(myUser => {
// Reload model to validate data
return myUser.reload(myUser => {
// Validate modified model
return myUser.validate(() => {
// Find user to give money to
return User
.findOne({ where: { username: sendUsername } }, { transaction: t })
.then(sendUser => {
// Increment balance
return sendUser
.increment('balance', { by: money, transaction: t })
.then(sendUser => {
// Reload model
return sendUser.reload(sendUser => {
// Validate model
return sendUser.validate(() => {
// Create a transaction for record-keeping
return Transaction
.create({
fromUser: myUser.id,
toUser: sendUser.id,
value: money
}, { transaction: t });
});
});
});
});
});
});
});
});
}).then(result => {
return done(null);
}).catch(err => {
return done(err);
});
This works, in that money is still transfered beetween people, but it still doesn't validate the models. I think the reason is that the .validate() and the .reload() methods do not have the ability to add the transaction: t parameter on it.
My question is if there's a way to do validation in a transaction, but I'd also like some help fixing this "callback hell." Again, I haven't done JS in a while, so there are probably better ways of doing this now that I'm just now aware of.
Thanks!
I believe you can't get validations to fire on the Model's increment and decrement and need to have instances. In some sequelize Model methods you can configure validations to run, but it doesn't look like it here
I'd do it like this
module.exports = async function(myUserId, sendUserId, money) {
const transaction = await connection.transaction();
try {
const [myUser, sendUser] = await Promise.all([
User.findById(myUserId, { transaction }),
User.findById(sendUserId, { transaction })
]);
await Promise.all([
myUser.increment('balance', {
by: money,
transaction
}),
myUser.increment('balance', {
by: -money,
transaction
})
]);
await Transaction.create({...}, { transaction })
await transaction.commit();
} catch(e) {
await transaction.rollback();
throw e;
}
}

Bluebird with mongoose using Promise.Each

I'm stuck in a function I'm working with ( I can be doing this all wrong ). So a quick explanation, I want to add bulk data in a collection, the collection is called "Sites" the format of the CSV is site,country,type. I'm trying to use promises for this (Bluebird). So consider the code:
Promise.each(sites, sites => new Promise((resolve, reject) => {
//console.log(sites);
let name = tools.extractDomain(req, res, sites[0]);
let country = sites[1];
let group = sites[2];
if (name != "" && country != "" && group != "") {
Site.findOne({ name: name }, "_id", function(err, duplicate) {
if (false) {
console.log("Duplicate site: " + duplicate);
} else {
//console.log("Adding " + name)
let site = new Site()
site.name = name
site.meta = {}
site.group = group
site.country = country
site.geomix = []
site.addedBy = req.user._id
site.addedAt = Date.now()
site.saveAsync().then(function(response){
tools.saveHistory(req, res, response._id, response.name, "Website Meta fetched.");
tools.saveHistory(req, res, response._id, response.name, "Link added for the first time."); //Save in history
resolve(site);
}).catch(function (e){
console.log(name);
reject();
});
}
});
}else{
console.log('Wrong Format');
}
}).then((data) => {
console.log('All websites processed!');
addedSites.push(data);
}).catch(err => {
//console.error('Failed');
}));
res.send({ status: 'ok', message: ''});
I'm making ajax calls so I return a res.send({ status: 'ok', message: ''}), I know that its in the incorrect place and I want to send some data along the res.send. Currently it sends the headers before the code actually finishes. I want to send the headers after all the data is added in Mongo but for every each in this case he resolve() so if I send the headers inside the ".then" of the ".each" I will get headers already sent error.
This might be a bit confusing. I feel I'm not doing this right. I'm going a bit crazy as well as I can't find a proper example that I can understand and implement.
But in the end my main question is: using an Ajax call what's the proper way to add let's say 1000 records in a collection using promises and actually control properly those who fail to add and those who don't?
Right now my code actually works but the logic is wrong for sure.
Thanks.
You can use bulkWrite on your model.
Ref: http://mongoosejs.com/docs/api.html#model_Model.bulkWrite
EDIT:
Sorry I misunderstood you. You need to move res.send({ status: 'ok', message: ''}); to then() and catch() blocks, so you will get something like this:
Promise.each(sites, sites => new Promise((resolve, reject) => {
// stuff you did before
}).then((data) => {
console.log('All websites processed!');
addedSites.push(data);
res.send({ status: 'ok', message: ''});
}).catch(err => {
res.send({ status: 'failed', message: err.message});
}));
This is what I came too, if someone can tell me if this is a good arch.
exports.addBulkSite = function(req, res, next) {
let siteArray = csv.parse((req.body.sites).trim()),
addedSites = [],
failedSites = [],
duplicated = [],
sites = siteArray,
size = sites.length,
processed = 0,
meta;
Promise.each(sites, sites => new Promise((resolve, reject) => {
let name = tools.extractDomain(req, res, sites[0]),
country = sites[1],
group = sites[2];
if (name != "" && country != "" && group != "") {
Site.findOneAsync({ name: name }, "_id").then(function(duplicate) {
duplicated.push(duplicate);
reject({name:name, message: 'Duplicated', critical:false});
}).catch(function(notDuplicated){
let site = new Site()
site = {
name: name,
meta: {},
group: group,
country: country, geomix:{},
addedBy: req.user._id,
addedAt:Date.now()
}
site.saveAsync().then(function(response){
tools.saveHistory(req, res, response._id, response.name, "Website Meta fetched.");
tools.saveHistory(req, res, response._id, response.name, "Link added for the first time."); //Save in history
resolve(site);
}).catch(function (e){
console.log(e);
reject({name:name, message: 'Error saving in the database. Please contact the administrator.', critical: true});
});
});
}else{
reject({name:name, message: 'Paramaters are missing', critical:false});
}
}).then((data) => {
processed++;
addedSites.push(data);
if(processed==size){
console.log('out');
res.send({ status: 'ok', addedSites: addedSites, failedSites: failedSites, duplicated: duplicated});
}
}).catch((err) => {
processed++;
console.log(err);
failedSites.push(err);
if(processed==size){
console.log('out');
res.send({ status: 'ok', addedSites: addedSites, failedSites: failedSites, duplicated: duplicated});
}
}));
}

Categories