Sequelize Transaction inside forEach loop - node.js

I'm trying to use transaction inside forEach loop using async/await syntax of Node 7.0+
When I try to print committed transaction response in console, I'm able to see the values but those same values are not committed in to DB.
Below is the code :
documentInfo.forEach(async (doc) => { // array of documentInfo
var frontImgName = await module.exports.uploadImage(docFiles, doc.front, req, res )
var backImgName = await module.exports.uploadImage(docFiles, doc.back, req, res )
var checkKycDoc = await KYCDocument.findOne({
where: {
kyc_id: checkUserKyc.dataValues.kyc_id,
user_id: checkUserKyc.dataValues.user_id
}
})
if (checkKycDoc) { //update
var updateDocument = await KYCDocument.update({
document_name: doc.document_name,
front_image: frontImgName,
back_image: backImgName
}, {
where: {
kyc_id: checkUserKyc.dataValues.kyc_id,
user_id: checkUserKyc.dataValues.user_id
},
}, {transaction})
log('updateDocument', updateDocument.dataValues)
} else { // insert
var newKycDocument = await new KYCDocument({
kyc_id: checkUserKyc.dataValues.kyc_id,
user_id: checkUserKyc.dataValues.user_id,
document_name: doc.document_name,
front_image: frontImgName,
back_image: backImgName,
status: true
}, {transaction})
log('newKycDocument', newKycDocument.dataValues)
}
if (rowCount === documentInfo.length) {
await transaction.commit() // transaction is printed on this line
log('KYC has been uploaded successfully')
helpers.createResponse(res, constants.SUCCESS,
messages.KYC_UPLOAD_SUCCESS,
{'error': messages.KYC_UPLOAD_SUCCESS}
)
} else {
rowCount++
}
})

The issue was in the create method.
To resolve the issue I had to create a new row using:
var newKycDocument = await KYCDocument.create({
kyc_id: checkUserKyc.dataValues.kyc_id,
user_id: checkUserKyc.dataValues.user_id,
document_name: doc.document_name,
front_image: frontImgName,
back_image: backImgName
}, {transaction})
I was missing the .create method.

Related

Transactions With Mongoose and NodeJs, calling an external function

I have some code that have implemented Transaction and it works perfectly, but now I need to call a function inside this code(it has a comment in my code example), both parts have to have success or then nothing have to be done. It is not working properly. How can I integrate the "sendCashback, function" to work perfectly with my Transaction?
This is my code:
var transaction: any
let cashback_sent: any
try{
const transactiontimectrl = Number(process.env.TRANSACTION_TIME_STOP)
let dt = new Date();
dt.setSeconds(dt.getSeconds() - transactiontimectrl);
const timectrl = dt.toISOString();
console.log('teste transaction1')
console.log(timectrl)
const session = await mongoose.startSession();
await session.withTransaction(async () => {
const transactionRes = await Transaction.findOne(
{
from: from,
//to: to,
//value: valuetodebit,
createdAt: {$gt: timectrl}
}).session(session)
if(!transactionRes){
transaction = await Transaction.create([
{
from,
to,
value: value,
division_factor: destinationUserGroup.division_factor,
title: title || null,
description: description || null,
type: type || null,
hasCashback: hasCashback,
realmoney: realmoney,
valuetodebit: valuetodebit
}],{ session })
}else{
throw new Error('Erro, uma transação semelhante foi realizada recentemente')
}
if (!transaction) {
throw new Error('Erro, tente novamente mais tarde')
}
console.log('#sadihjaisvq3')
let fromBalance
//Saldo real
if (realmoney) {
fromBalance = await Balance.findOne({
type: IBalanceType.GENERAL,
user: from
}).session(session)
}
//Saldo cashback
else {
fromBalance = await Balance.findOne({
type: IBalanceType.CASHBACK,
user: from
}).session(session)
}
console.log('****criar balance2')
let toBalance = await Balance.findOne({
type: IBalanceType.GENERAL,
user: to
}).session(session)
console.log('****criar balance1', toBalance)
if (!toBalance) {
console.log('****criar balance')
toBalance = await Balance.create(
{
user: to
})
}
let toLivetPay = await Balance.findOne({
type: IBalanceType.GENERAL,
user: toLivet
}).session(session)
if (!fromBalance || !toBalance || !toLivetPay) throw new Error()
if (!credit) {
fromBalance.value = fromBalance.value + parseFloat(valuetodebit) * -1
}
// fromBalance.value = fromBalance.value + parseFloat(valuetodebit) * -1
toBalance.value = toBalance.value + parseFloat(value)
let valorCash = Number(value) / 0.75
let valorLivet = Number(valorCash) * 0.25
toLivetPay.value = toLivetPay.value + parseFloat(String(valorLivet))
await fromBalance.save()
await toBalance.save()
await toLivetPay.save()
if (hasCashback) {
if (!saque) {
//EXTERNAL FUNCTION---------------------------------------------------------
cashback_sent = await sendCashback(
transaction[0],
body.originUser!,
body.destinationUser!
)
//------------------------------------------------------------------------
}
if (!cashback_sent) {
throw new Error('Erro ao distribuir cashback')
}
console.log('depois do cashback')
}
})
The transaction advanced usage is what you need. To work with async await, checkout this link.
As described in the linked mongoose documentation, you can call session.startTransaction() method to start a transaction, and whenever you wish to abort, call session.abortTransaction() to abort the transaction, after all the operations in the transaction are success, call session.commitTransaction() to commit the transaction.

Iterate dataArray to create an object is not happening

How can I parse the incoming nomData data array and store those values into an object along with userEmail ? Somehow below code is not working, could someone pleas advise the issue here.
Expected database columns values:
var data = { useremail: userEmail, nomineeemail: email, nomineename: name, nomineeteam: team, reason: reason }
server.js
app.post('/service/nominateperson', async (req, res) => {
try {
const userEmail = req.body.userEmail;
const nomData = req.body.nomRegister;
const formData = {
useremail: userEmail,
nomineeemail: {},
nomineename: {},
nomineeteam: {},
reason: {}
}
const newArray = nomData.map(item => {
formData.nomineeemail = item.email;
formData.nomineename = item.name;
formData.nomineeteam = item.team;
formData.reason = item.reason;
});
var data = { useremail: userEmail, nomineeemail: email, nomineename: name, nomineeteam: team, reason: reason }
// Ideally I should get nomData
//items parsed create an data object and pass that into bulkCreat() method:
const numberOfNominations = await NominationModel.count({where: {useremail: userEmail}});
if (numberOfNominations <= 3) {
const nominationData = await NominationModel.bulkCreate(data);
res.status(200).json({message: "Nomination submitted successfully !"});
} else {
res.status(202).json({message: "Nomination limit exceeded, please try next week !"});
}
} catch (e) {
res.status(500).json({fail: e.message});
}
});
So i assume nomData is an array containing multiple nominations. So if you want to bulkCreate with data you should pass an array instead of an object.
const data = nomData.map(item => ({
useremail: userEmail,
nomineeemail: item.email,
nomineename: item.name,
nomineeteam: item.team,
reason: item.reason
}));
...
await NominationModel.bulkCreate(data)

NodeJS Async/Await doesn't await (returns Promise pending)

I'm writing an API that gets past transactions on the Stellar network for a user, looks up the corresponding users in my database, and put's it all together into a JSON (to respond to the request - later).
Problem: looking up the username corresponding to the accountID ("from" field) is an async method with mongoose and only returns data after the JSON has been assembled.
I've tried to use async/await, promises, .thens but nothing seems to work.
server.payments()
.forAccount(accountId)
.cursor('now')
.order('desc')
.limit(2)
.call()
.then(function (page) {
var history = []
for(var i = 0; i<page.records.length; i++){
var obj = page.records[i]
//Get the username corresponding to the key from my database
//FIXME
var username
findUser(obj["from"]).then(result => {
username = result
})
var payment = {
"currency":obj["asset_code"],
"from": obj["from"],
"username":username,
"amount": obj["amount"],
"timestamp":obj["created_at"]
}
history.push(payment)
}
console.log(history)
//console.log(JSON.stringify(history))
})
.catch((err) => {
console.log(err)
})
async function findUser(senderKey){
var user = await mongoose.User.findOne({publicKey: senderKey})
console.log(user.username)
return user.username
}
Expected result: findUser returns value, payment variable uses it and gets logged together.
What happens: findUser starts looking for data, payment variable gets put together (username as undefined) and logged, findUser returns data.
Here's the log: (spiderman is the actual username from my database)
[ { currency: 'MEUR',
from: 'GACRQARPR2OMWRG6IH7HM5DYTA3FMM6UKA7NKS4BIJIADRIKFRPAIE7G',
username: undefined,
amount: '3.0000000',
timestamp: '2019-05-07T13:37:04Z' },
{ currency: 'MEUR',
from: 'GACRQARPR2OMWRG6IH7HM5DYTA3FMM6UKA7NKS4BIJIADRIKFRPAIE7G',
username: undefined,
amount: '2.0000000',
timestamp: '2019-05-07T13:34:21Z' } ]
spiderman
spiderman
Highlight recommend, you can make it easy with async/await syntax for your case.
You will wait until the server responses the page, then each of obj in page.records (.map or .forEach... will make you some mess), you wait for findUser function returns the username, then do your business.
try {
const page = await server.payments()
.forAccount(accountId)
.cursor('now')
.order('desc')
.limit(2)
.call()
const history = [];
for (const obj of page.records) {
const username = await findUser(obj["from"]);
const payment = {
"currency": obj["asset_code"],
"from": obj["from"],
"username": username,
"amount": obj["amount"],
"timestamp": obj["created_at"]
}
history.push(payment)
}
console.log(history)
} catch (e) {
console.log(e)
}
Remind: await expression only allowed within an async function.
You can use the new "for of" loop along with async await :-
server.payments()
.forAccount(accountId)
.cursor('now')
.order('desc')
.limit(2)
.call()
.then(async function (page) {
var history = []
for(const obj of page.records) {
const username = await findUser(obj["from"]);
var payment = {
"currency":obj["asset_code"],
"from": obj["from"],
"username":username,
"amount": obj["amount"],
"timestamp":obj["created_at"]
}
history.push(payment)
}
console.log(history)
//console.log(JSON.stringify(history))
})
.catch((err) => {
console.log(err)
})
You need a conditional that checks whether the function you are awaiting has completed
async function findUser(senderKey){
var user = await mongoose.User.findOne({publicKey: senderKey})
if (user){
console.log(user.username)
return user.username
}
}

node.js blocking, non-blocking issue in for statement

How can I save all of the json into my mongoldb?
Strangely, only the first value is stored every time.
It might be blocking/non-blocking issue.
json = {
["name":"Karl","id":"azo0"],
["name":"Robert","id":"bdd10"],
["name":"Joan","id":"difj90"],
["name":"Hallyn","id":"fmak88"],
["name":"Michael","id":"vma91"]
};
for(var i = 0; i < json.length; i++){
id = json[i].id;
name = json[i].name;
var ctx = {"id":id,"name":name};
db.json_db.count(ctx).exec(function(err, count) {
if(count < 1){
var model = new User({
"name":json[i].name,
"id":json[i].id
});
model.save(function(){
console.log("ok"+i);
});
}
});
};
After inserting, all of datas are filled with ["name":"Karl","id":"azo0"]
To check out console.log("ok"+i), it prints always "ok0" not "ok1", "ok2", "ok3"... etc..
How can I prevent this issue?
Incase you're using Async package, this is an best way to solve your problem...
async.eachSeries(json, (item, done) => {
let user = new User(
{
"name":json[i].name,
"id":json[i].id
},
(err, user) => {
if(err){
// handle err
}
return done();
}
);
});
.exec() tells me you're using Mongoose. So your loop can rewritten as:
const json = [
{name: "Karl", id: "azo0"},
{name: "Robert", id: "bdd10"},
{name: "Joan", id: "difj90"},
{name: "Hallyn", id: "fmak88"},
{name: "Michael", id: "vma91"}
];
for (const item of json) {
const count = await db.json_db.count(item).exec()
if (!count) {
await new User(item).save()
}
}
Error handling omitted.
See http://exploringjs.com/es2016-es2017/ch_async-functions.html

sequelize.js - Find by id and return result

I have a function,
var findUserDevice = function(userDeviceId){
var device = db.DeviceUser.find({
where: {
id: userDeviceId
}
}).then(function(device) {
if (!device) {
return 'not find';
}
return device.dataValues;
});
};
but this function does not return anything...
var UserDevice = findUserDevice(req.body.deviceUserId);
console.log(UserDevice);// undefined
The operation you are trying to do is async, which means that you need to use a callback. Since sequelize is build on top of Promises, you should actually write your code like this :
var findUserDevice = function(userDeviceId){
// return the promise itself
return db.DeviceUser.find({
where: {
id: userDeviceId
}
}).then(function(device) {
if (!device) {
return 'not find';
}
return device.dataValues;
});
};
And later use it like :
findUserDevice(req.body.deviceUserId).then( function(UserDevice) {
console.log(UserDevice);
});
It's 2020, async & await are becoming more popular. You can change your code to:
const findUserDevice = async function (userDeviceId) {
const device = await db.DeviceUser.findOne({
where: {
id: userDeviceId
}
});
if (device === null) {
return 'device not found';
}
return device.dataValues;
};
(async () => {
// ...
const UserDevice = await findUserDevice(req.body.deviceUserId);
console.log(UserDevice);
// ...
})()
IMHO, the code above is way more readable.
If you are getting undefined instead of 'not find' on the console, it means your function is returning a value. The problem might be dataValues is actually undefined. You need to check for the content of device.
Hint: Try returning just device or device.id
PS. If you want to do the search based on id, should go for findById() function of your model.
var device = db.DeviceUser.findById(userDeviceId).then(function(device) {
if (!device) {
return 'not find';
}
return device.dataValues;
});
This function received params id, this worker for me:
const { customer } = require('../models');
const get = async function(req, res){
let id = req.params.id;
[err, singleCustomer] = await to(customer.findByPk(id, { raw : true }));
return ReS(res, { message :'Obtener cliente: : ', data : JSON.stringify(singleCustomer) });
}

Resources