I am making an API for online car sales. I need to delete a some data about car sales at the same time with sequelize using sequelize transaction. There are 2 conditions. First condition if a car is sold, then i need to update the car status but at the same time create the purchase and feedback data. If a car is already sold somewhere else, then i need to delete the car, the amount of views, likes, and bargain data of the car, and create a feedback about the sales of the car.
If, for example, the 3rd data of the car that need to be deleted violates a constraint (causes an error), then the deletion of the first and second data of that car will be undone along with any deleted / created data.
I have tried using the sequelize transaction but it commits even if there are any data that haven't deleted yet (caused an error)
this is the code that was already made.
async function deleteCarAsAdmin(req, res) {
let error = [];
try {
// id, reasonType, comment, price, paymentMethod, buyerUserType, sellDate
const { deleteData } = req.body;
const trans = await sequelize.transaction();
const lengtData = deleteData.length;
function customForEach(callback) {
const array = this;
let index = 0;
const next = () => {
index ;
if (array.length > 0) {
callback(array.shift(), index, next);
}
};
next();
}
Array.prototype.customForEachFunc = customForEach;
const promise = await new Promise((resolve, reject) => {
const resRej = (errlength, lengtData, deleteDatalength) => {
if (errlength == lengtData) {
return reject({ message: error });
}
if (deleteDatalength == 0) {
return resolve();
}
};
deleteData.customForEachFunc(async (data, index, next) => {
const carData = await models.Car.findByPk(data.id, { transaction: trans });
if (!carData) {
apiResponse._redconsole(`GA KETEMU BANG / Car Id ${data.id} Not Found`);
error.push(`carID : ${data.id} Not Found`);
console.log(`index : ${index}`);
console.log(`length ${deleteData.length}`);
console.log(error);
resRej(error.length, lengtData, deleteData);
return next();
}
if (validator.isInt(data.id ? data.id.toString() : '') === false) {
apiResponse._redconsole(`Car Id ${data.id} MustBe Integer`);
error.error.push('Id Must Be an Integer,\n');
resRej(error.length, lengtData, deleteData);
return next();
}
if (!data.reasonType) {
apiResponse._redconsole(`Please Provide Your Reason For Deleting carId ${data.id}!`);
error.push(`carID : ${data.id} Please Provide Your Reason For Deleting carId ${data.id}`);
resRej(error.length, lengtData, deleteData);
return next();
}
let regex = /^(?!\s*$). /;
if (!data.comment || !regex.test(data.comment)) {
apiResponse._redconsole(`Please Provide Your Comment For Deleting carId ${data.id}!`);
error.push(`Please Provide Your Coment For Deleting carId ${data.id}`);
resRej(error.length, lengtData, deleteData);
return next();
}
let reasonId = null;
const getReasonType = await models.ReasonType.findOne(
{
where: { typeReason: data.reasonType },
attributes: ['id']
},
{ transaction: trans }
).catch(async err => {
apiResponse._redconsole(err.message`1`);
error.push(`${err.message}\n`);
resRej(error.length, lengtData, deleteData);
return next();
});
if (getReasonType) {
reasonId = getReasonType.id;
}
const now = moment(data.sellDate)
.tz('Asia/Jakarta')
.format('YYYY-MM-DD HH:mm:ss');
if (data.reasonType === 1) {
if (!data.price) {
apiResponse._redconsole(`Please Provide Your Price For CarId ${data.id}`);
error.push(`Please Provide Your Price For carId ${data.id},\n`);
resRej(error.length, lengtData, deleteData);
return next();
}
if (!data.paymentMethod) {
apiResponse._redconsole(`Please Provide Your Payment Method For CarId ${data.id}`);
error.push(`Please Provide Your Payment Method For carId ${data.id},\n`);
resRej(error.length, lengtData, deleteData);
return next();
}
if (!data.sellDate) {
apiResponse._redconsole(`Please Provide Your Payment Date For CarId ${data.id}`);
error.push(`Please Provide Your Payment Date For carId ${data.id},\n`);
resRej(error.length, lengtData, deleteData);
return next();
}
if (!data.buyerUserType) {
apiResponse._redconsole(`Please Provide Your Type Of Customer For CarId ${data.id}`);
error.push(`Please Provide Your Type Of Customer For carId ${data.id},\n`);
resRej(error.length, lengtData, deleteData);
return next();
}
await models.Purchase.create(
{
userId: carData.userId,
carId: data.id,
price: data.price,
paymentMethod: data.paymentMethod
},
{ transaction: trans }
).then(async datas => {
//update car status
await datas
.update(
{
status: 2
},
{
where: { id: datas.carId }
},
{ transaction: trans }
)
.catch(async err => {
apiResponse._redconsole(`${err.message} carId ${data.id}`);
error.push(` ${err.message},\n`);
await trans.rollback();
resRej(error.length, lengtData, deleteData);
return next();
});
//create feedback
await models.FeedBack.create(
{
carId: data.id,
userId: carData.userId,
reasonType: reasonId,
comment: data.comment,
paymentMethod: data.paymentMethod == 0 ? 'kredit' : 'tunai',
buyerUserType: data.buyerUserType,
sellDate: now
},
{ transaction: trans }
).catch(async err => {
apiResponse._redconsole(`${err.message} carId ${data.id}`);
error.push(` ${err.message},\n`);
await trans.rollback();
resRej(error.length, lengtData, deleteData);
return next();
});
await models.Car.destroy(
{
where: { id: data.id }
},
{ transaction: trans }
).catch(async err => {
apiResponse._redconsole(`${err.message} carId ${data.id}`);
error.push(` ${err.message},\n`);
await trans.rollback();
resRej(error.length, lengtData, deleteData);
return next();
});
});
} else if (data.reasonType == 2) {
//create feedback
await models.FeedBack.create(
{
carId: data.id,
userId: carData.userId,
reasonType: reasonId,
comment: data.comment
},
{ transaction: trans }
).catch(async err => {
apiResponse._redconsole(`${err.message} carId ${data.id}`);
error.push(` ${err.message},\n`);
resRej(error.length, lengtData, deleteData);
await trans.rollback();
return next();
});
//delete car
await models.Car.destroy(
{
where: { id: data.id }
},
{ transaction: trans }
).catch(async err => {
apiResponse._redconsole(`${err.message} carId ${data.id}`);
error.push(` ${err.message},\n`);
resRej(error.length, lengtData, deleteData);
await trans.rollback();
return next();
});
//delete bargain
const bargainData = await models.Bargain.findOne({
where: {
carId: data.id
}
});
if (bargainData) {
await models.Bargain.destroy(
{
where: { carId: data.id }
},
{ transaction: trans }
).catch(async err => {
apiResponse._redconsole(`${err.message} carId ${data.id}`);
error.push(` ${err.message},\n`);
await trans.rollback();
resRej(error.length, lengtData, deleteData);
return next();
});
}
//delete like
console.log('delete like');
const likeData = await models.Like.findOne({
where: {
carId: data.id
}
});
if (likeData) {
await models.Like.destroy(
{
where: { carId: data.id }
},
{ transaction: trans }
).catch(async err => {
apiResponse._redconsole(`${err.message} carId ${data.id}`);
error.push(` ${err.message},\n`);
await trans.rollback();
resRej(error.length, lengtData, deleteData);
return next();
});
}
//delele view
console.log('delete view');
console.log(data.id);
const viewData = await models.View.findOne({
where: {
carId: data.id
}
});
console.log(viewData);
if (viewData) {
await models.View.destroy(
{
where: { carId: data.id }
},
{ transaction: trans }
).catch(async err => {
apiResponse._redconsole(`${err.message} carId ${data.id}`);
error.push(` ${err.message},\n`);
await trans.rollback();
resRej(error.length, lengtData, deleteData);
return next();
});
}
}
resRej(error.length, lengtData, deleteData);
return next();
});
});
await trans.commit();
return res.status(200).json({
success: true
});
} catch (err3) {
return res.status(500).json({
success: false,
errors: err3.message
});
}
}
Please help. Thank you, and sorry for the broken english
CodePudding user response:
At least I noticed you mixed up await
with then/catch
. Use only await
and wrap it into try/catch
if needed.
Also in await datas.update
you should indicate transaction
in the second parameter and not in the third one:
await datas
.update(
{
status: 2
},
{
where: { id: datas.carId },
transaction: trans
}
)
The same goes for destroy
:
await models.Car.destroy(
{
where: { id: data.id },
transaction: trans
}
)
...
await models.Bargain.destroy(
{
where: { carId: data.id },
transaction: trans
}
)
...
await models.Like.destroy(
{
where: { carId: data.id },
transaction: trans
}
)
...
await models.View.destroy(
{
where: { carId: data.id },
transaction: trans
}
)
and you forgot to indicate a transaction in findOne
:
const bargainData = await models.Bargain.findOne({
where: {
carId: data.id
},
transaction: trans
})
...
const likeData = await models.Like.findOne({
where: {
carId: data.id
},
transaction: trans
})
...
const viewData = await models.View.findOne({
where: {
carId: data.id
},
transaction: trans
});