Node.js MS SQL transaction - node.js

Can anyone help to implement MS SQL transactions in Node.js . I am try to execute multiple stored procedures inside a promise.
Method 1
const executeProcedure = async (data1, data2) => {
try {
// sql connection
let dbConn = new sql.ConnectionPool(config));
await dbConn.connect();
let transaction = new sql.Transaction(dbConn);
await transaction.begin().then(async()=> {
// tranaciton create
// begin tran
let result = await insertOperation(transaction, data1);
let result2 = await updateOperation(transaction, data2);
let result1 = await Promise.all([result, result2]);
await transaction.commit();
dbConn.close();
}).catch(async(err)=> {
await transaction.rollback();
dbConn.close();
throw err;
});
return {};
}
catch (error) {
throw(error);
}
}
method 2
const insertOperation = async (transaction,data1) => {
return new Promise((resolve, reject) => {
try {
var request = new sql.Request(transaction);
request.input('data1' , sql.NVarChar(40) , data1)
.execute('dbo.insertOperation').then((recordSet) => {
resolve(recordSet.recordsets);
}).catch((err) => {
reject(err);
});
}
catch (error) {
reject(error);
}
});
}
method 3
const updateOperation = async (transaction,data2) => {
return new Promise((resolve, reject) => {
try {
var request = new sql.Request(transaction);
request.input('data2' , sql.NVarChar(40) , data2)
.execute('dbo.updateOperation').then((recordSet) => {
resolve(recordSet.recordsets);
}).catch((err) => {
reject(err);
});
}
catch (error) {
reject(error);
}
});
}
Now I get this error
Can't rollback transaction. There is a request in progress.
anybody please help me to solve this problem

You make some unnecessary Promise wrapper.
Example below:
const insertOperation = async (request, data1) => {
request.input("data1", sql.NVarChar(40), data1);
const result = await request.execute("dbo.insertOperation");
return result.recordsets;
};
const updateOperation = async (request, data2) => {
request.input("data2", sql.NVarChar(40), data2);
const result = await request.execute("dbo.updateOperation");
return result.recordsets;
};
const executeProcedure = async (data1, data2) => {
// sql connection
const dbConn = new sql.ConnectionPool(config);
await dbConn.connect();
let transaction;
try {
transaction = new sql.Transaction(dbConn);
await transaction.begin();
const request = new sql.Request(transaction);
const results = await Promise.all([
insertOperation(request, data1),
updateOperation(request, data2),
]);
await transaction.commit();
return results;
} catch (err) {
await transaction.rollback();
throw err;
} finally {
await dbConn.close();
}
};

#ikhvjs please check the below use case as well
try {
request.input("data", sql.NVarChar(40), data1);
const result = await request.execute("dbo.insertOperation");
return result.recordsets;
} catch (err) {
throw err;
}
};
const updateOperation = async (request, data2) => {
try {
request.input("data", sql.NVarChar(40), data2);
const result = await request.execute("dbo.updateOperation");
return result.recordsets;
} catch (err) {
throw err;
}
};
const executeProcedure = async (data1, data2) => {
try {
// sql connection
const dbConn = new sql.ConnectionPool(config);
await dbConn.connect();
const transaction = new sql.Transaction(dbConn);
try {
await transaction.begin();
const request = new sql.Request(transaction);
const results = await Promise.all([
insertOperation(request, data1),
updateOperation(request, data2),
]);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
} finally {
await dbConn.close();
}
} catch (error) {
throw error;
}
};```

Related

Await Promise.all async does't work in my code

I have a lambda function with the structure below,
It used to work in older versions of nodejs but it doesn't work with the newer versions.
I know my code structure is quite messy and wrong but I can't get my head around it. I'm trying to use Promise.all but I'm obviously doing something wrong cause it's not getting executed at all.
By the way, I'm not getting any errors. The promise.all method never gets executed.
let AWS = require('aws-sdk');
exports.handler = async(event, context, callback) => {
let result = {};
try {
result = await getOrder(sql, 0);
result.map(
(dataField) => {
});
}
catch (error) {
console.log(error);
callback(error);
}
var today_result = [];
const groupKey = i => i.user_id + '_' + i.when;
const counts = _.countBy(followingsIDs, groupKey);
const isMulti = i => counts[groupKey(i)] > 1;
const multiPropkey = i => ({ multiplekey: isMulti(i) ? groupKey(i) : groupKey(i) });
const multiProp = i => ({ multiple: isMulti(i) ? counts[groupKey(i)] : 1 });
const updated = _.map(followingsIDs, i => _.extend(i, multiProp(i), multiPropkey(i)));
const uniqResult = _.uniq(updated, function(d) { return d.multiplekey });
// Doesn’t execute from here —>
await Promise.all(uniqResult.map(async(dataField) => {
console.log("test_");
dosomething()
if (true) {
let sql = `INSERT INTO ….`
result = await getOrder(sql, 0);
try {
const data = await sns.publish(params).promise();
}
catch (e) {
console.log(e.stack);
response.result = 'Error';
}
}
}));
// Till here <----
callback(null, uniqResult);
};
let getOrder = async(sql, params) => {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) throw err;
connection.query(sql, params, (err, results) => {
if (err) {
reject(err);
}
// console.log("-----Query Done!");
connection.release();
// console.log("-----Data: ", results);
resolve(results);
});
});
});
};
What are you awaiting to? The uniqResult is just declared as an empty array. Immediately after that you pass it to Promise.all. You need to fill it with Promises and then pass it to Promise.all.

Can't insert JSON file into MongoDB's collection throught Node driver

I'm trying to read files from my disk and push it into MongoDB's collections, but connection closing before it done and I get error: MongoError: Topology is closed, please connect.
async function launch() {
try {
await mongo.connect();
console.log("Connection established");
const database = mongo.db('task');
const firstCol = database.collection('first');
const secondCol = database.collection('second');
const insertIntoCollection = async (file, col) => {
fs.readFile(file, async function(err, data) {
if (err) throw err;
const json = JSON.parse(data);
const result = await col.insertMany(json);
console.log(result.insertCount);
});
}
await insertIntoCollection('data/first.json', firstCol);
await insertIntoCollection('data/second.json', secondCol);
} finally {
await mongo.close();
}
}
launch().catch(console.dir);
What am I doing wrong?
In the above case mongo client will close before the insertIntoCollection function trigger since it is a promise function and promise will not over before the finally trigger.I hope below code will fulfil your expectations.
async function launch() {
try {
await mongo.connect();
console.log("Connection established");
const database = mongo.db('task');
const firstCol = database.collection('first');
const secondCol = database.collection('second');
const insertIntoCollection = async (file, col) => {
return new Promise((resolve, reject) => {
fs.readFile(file, async (err, data) => {
try {
if (err) reject(err);
const json = JSON.parse(data);
const result = await col.insertMany(json);
console.log(result.insertCount);
resolve(result.insertCount)
} catch (err) {
reject(err)
}
});
})
}
await insertIntoCollection('data/first.json', firstCol);
await insertIntoCollection('data/second.json', secondCol);
} finally {
await mongo.close();
}
}
launch().catch(console.dir);

problem in sending base64 data in GET request

Hi I am facing issues sending base64 data in GET request.
I was successful in converting the image into base64 data and inserting it in receivedFile
but during response the attachments come as an empty array while the rest of the data i.e user_id is flowing successfully.
Hence if you could please help me to resolve this issue.
Below is the code
router.js
router.get('/users/data/expand/:nid',async (req,res) => {
var idselected = req.params.nid;
var dir = '\images';
var receivedFile = [];
try {
const checkData = await user.find({"user_id": idselected});
await checkData[0].attachments.forEach (element => {
fs.readdir(dir,function(err,files) {
if(err) {
console.log(err)
}else {
files.forEach((filename) => {
filename = element;
fs.readFile(filename,'base64', (err,base64Data) => {
if(err) {
console.log(err);
}
receivedFile.push(base64Data);
})
})
}
})
})
//issue is here the attachments is coming as empty instead of base64 data
const returnUser = new User({
user_id: checkData.user_id,
attachments: receivedFile
})
res.status(201).send(returnUser);
}
catch(e) {
res.status(500).send(e)
}
})
Well its always good to create helper functions and to promisfy it so you can use async / await syntax.
I have changed your code. I didnt tested it but i guess it should work:#
router.get("/users/data/expand/:nid", async (req, res) => {
var idselected = req.params.nid;
var dir = "images";
try {
const checkData = await user.findOne({ user_id: idselected });
let receivedFile = await Promise.all(
checkData.attachments.flatMap(async element => {
let files = await readDirectory(dir);
return await Promise.all(
files.map(filename => {
filename = element;
return readFile(filename)
})
);
})
);
const returnUser = new User({
user_id: checkData.user_id,
attachments: receivedFile
});
let savedUser = await returnUser.save();
res.status(201).send(savedUser);
} catch (e) {
res.status(500).send(e);
}
});
function readDirectory(dir) {
return new Promise((res, rej) => {
fs.readdir(dir, function(err, files) {
if (err) {
rej(err);
} else {
res(files);
}
});
});
}
function readFile(filename) {
return new Promise((res, rej) => {
fs.readFile(filename, "base64", (err, base64Data) => {
if (err) {
rej(err);
}
res(base64Data);
});
});
}
I guess you use mongoose.
There is an method called findOne and also you forgot to save your model with returnUser.save()

nodejs mssql transaction pool

I have a typescript module.
public multipleQuery(queries: string[]) {
return new Promise(async (resolve, reject) => {
const cPool = new sql.ConnectionPool(this.room.db);
await cPool.connect().then((pool: any) => {
const transaction = new sql.Transaction(pool);
return transaction.begin(async (err: any) => {
const request = new sql.Request(transaction, { stream: true });
try {
queries.forEach(async (q) => {
await request.query(q);
});
transaction.commit((err2: any) => {
pool.close();
if (err2) {
reject(err2);
} else {
resolve(true);
}
});
} catch (err) {
transaction.rollback(() => {
pool.close();
reject(err);
});
}
});
}).catch((err: Error) => {
cPool.close();
reject(err);
});
});
}
queries variable is an array of string, I put inside a lot of sql insert queries.
No matter what I write in queries, I still receive this error, why?
RequestError: Requests can only be made in the LoggedIn state, not the
SentClientRequest state TransactionError: Can't acquire connection for
the request. There is another request in progress.
the solutions is to use async
const async = require("async");
public multipleQuery(queries: string[]) {
return new Promise((resolve, reject) => {
const pool = new sql.ConnectionPool(this.room.db);
return pool.connect().then((p: any) => {
const transaction = new sql.Transaction(p);
return transaction.begin((err: any) => {
const request = new sql.Request(transaction);
if (err) {
reject(err);
}
return async.eachSeries(queries, async (query: any, callback: any) => {
return request.query(query);
}, async (err2: any) => {
if ( err2 ) {
await transaction.rollback(() => {
pool.close();
reject(err2);
});
} else {
await transaction.commit(() => {
pool.close();
resolve(true);
});
}
});
});
});
});
}

Why does this promise code never run?

As you can see in the .gif below, I call tape.js twice in my integration tests and it works fine. But the second time I run the tests, after I've commented out the code to insert test data into my database, the body of the tape.js function never runs.
Why does this happen?
I've put a breakpoint in my database code and it finishes and calls resolve() before the next tape.js test runs so I don't get it.
Here's my test class, followed by database class:
import tape = require('tape');
const testData = require('../../../helper/data');
import * as db from '../../../helper/integration/database';
helper.setTestEnvironmentVariableOn();
const startedServer : hapi.Server = require('../../../../src/app');
(async function runTests() {
recreateDatabase();
await db.runQuery(getInsertGetRolesTestPermissionsSql());
await test_getRoles_returnsAllRoles();
recreateDatabase();
await db.runQuery(getInsertGetUsersRolesTestPermissionsSql());
await test_getUsersRoles_userCanGetHerOwnRoles();
await startedServer.stop();
})();
function test_getRoles_returnsAllRoles() {
return new Promise( (resolve, reject) => {
tape(testeeName + 'GET /roles returns all, and only, Ekaya roles', async (assert) => {
await helper.createNewUser();
superagent
.get(testData.url + `roles`)
.set('token', testData.token)
.end( (error: any , result: superagent.Response) => {
assert.equals(error, null);
assert.end();
resolve();
});
});
});
}
function test_getUsersRoles_userCanGetHerOwnRoles() {
return new Promise( (resolve, reject) => {
tape(testeeName + 'GET /users/id/roles gives a user her own roles', async (assert) => {
const userid = '635de6dc-0df9-43f4-96dc-922bca541515';
const token = await helper.getTokenForUser(userid);
superagent
.get(testData.url + `users/`+userid+`/roles`)
.set('token', token)
.end( (error: any , result: superagent.Response) => {
assert.equals(error, null);
assert.end();
resolve();
});
});
});
}
function getInsertGetUsersRolesTestPermissionsSql() : string {
return `
INSERT INTO enum.permissions(permissions_id, name,...
`;
}
function getInsertGetRolesTestPermissionsSql() {
return `
delete fr...`;
}
Database class:
import * as pg from "pg";
import 'reflect-metadata';
const Config = require('../../../src/config');
const config = new Config();
export function runQuery(sql: string) : Promise<any> {
return new Promise<any>(async (resolve, reject) => {
try {
const _db = new pg.Client(config.ekaya_local_connection_string);
await connect(_db);
_db.query(sql, function(error, result) {
if (error) {
console.error('Error running query', error);
_db.end();
reject(error);
return;
}
_db.end();
resolve(result);
});
}
catch (error) {
reject(error);
}
});
}
function connect(_db: pg.Client) : Promise<void> {
return new Promise<void>( function(resolve, reject) {
try {
_db.connect(function(error) {
if (error) {
console.error('Could not connect to postgres in integration test helper', error);
reject(error);
return;
}
resolve(null);
});
}
catch (error) {
reject(error);
}
});
}
UPDATE - after vitaly-t's suggestion
New database code, no effect on the problem:
import 'reflect-metadata';
const Config = require('../../../src/config');
const config = new Config();
const pgp = require("pg-promise")();
export function runQuery(sql: string) : any {
return new Promise<any>(async (resolve, reject) => {
try {
const _db = pgp(config.ekaya_local_connection_string);
const result = await _db.query(sql);
pgp.end();
resolve({rows: result});
}
catch (error) {reject(error);}
});
}

Resources