Test all functions that have been terminated - javascript

I am developing a synchronizer between two databases (SQL Server and MySQL) with ElectronJS and Node.JS and everything is working fine, but I would like to execute the window.close() method to terminate the application when all tables are syncronized (which are done asynchronously within a for loop).
// I removed error and bank connection treatments because they are working
// Read json file that contains all tables and its columns (keys)
fs.readFile(ABSPATH + 'tables.json', 'utf8', async (err,json) => {
// Parse the content of the file into a JSON object
let vetor = JSON.parse(json)
// Foreach table
for (let i = 0; i < vetor.length; i++) {
// Read the table from SQL Server and save it falues into MySQL
await read(vetor[i].table,vetor[i].keys)
}
// Instead of closing, I'm just displaying this message on screen (for debugging)
document.body.innerHTML += "<h2>All data where inserted</h2>"
})
But as you can see, it is returning the final result before returning the functions, that is, they remain asynchronous:
Click here to see the image
I believe my error is at the time of saving the data in the following function because I tested it with console.log(), but I still can't make it synchronous:
con.connect(async err => {
// Begin MySQL Transaction
await con.beginTransaction(async err => {
// Clear the table
await con.query(`TRUNCATE TABLE ${table}`, err => {})
// Loop to insert all inputs into table
for (let i = 0; i < values.length; i++){
// Create and execute the query to input values into table
await con.query(createInsert(table,keys,values[0]), err => {})
}
// When all data are inputed, end Transaction
await con.commit(err => {
// Write in window that everything gonna allright
document.body.innerHTML += "<p>All data where successfully saved into " + table + ".</p>"
})
})
// End MySQL Transaction
})

Like georg had told me, the problem was that I was trying to await async functions, but the await only works with Promises.
I found the answer greetings to him and to this answer that I had received on StackOverflow in portuguese.
So, I just put my connection into a Promise, like this:
// Save datas into MySQL Database
function save(table, keys, values) {
return new Promise((res, rej) => {
// MSSQL returns a JSON object filled with informations that I don't want
// so I filter only what I want: the result of the query
values = values.recordset
// Start a new MySQL connection
let con = mysql.createConnection({
user: OUSER,
password: OPASS,
server: OHOST,
port: OPORT,
database: ODB
})
con.connect(err => {
if (err) {
alert(err)
window.close()
}
con.beginTransaction(err => {
// This function is just for DontRepeatYourself
rollBack(con, err, rej)
con.query(`TRUNCATE TABLE ${table}`, err => { rollBack(con, err, rej) })
con.query(createInsert(table,keys,values[0]), err => { rollBack(con, err, rej) })
con.commit(err => {
rollBack(con, err, rej)
document.body.innerHTML += `<p>All data where successfully registered into ${table}.</p>`
res()
})
})
})
})
}

Related

I'm unable to send a response to my react.js using http.get in node

I'm trying to get the temperature data from my node.js backend sent to react.js but i kept getting res.send is not a funtion
Sample code here
app.get("/gettemperature", (req, res) => {
const email = req.query.email;
let stmt = `SELECT * FROM users WHERE email=?`;
let todo = [email];
db.query(stmt, todo, (err, results, fields) => {
if (err) {
console.error(err.message);
}
if(results.length > 0 ){
let id = results[0].id;
let getID = `SELECT * FROM controlModules WHERE deviceowner=?`;
let getidData = [id];
db.query(getID, getidData, (err, resulta, fields) => {
if (err) {
console.error(err.message);
}
if(resulta.length > 0){
let lanip = resulta[0].ipaddress;
let url = "http://"+lanip+"/data";
http.get(url,(res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
try {
let json = JSON.parse(body);
const temp_actual = json.temperature.value;
console.log(temp_actual);
res.setHeader('Content-Type', 'application/json');
res.end(
JSON.stringify({
value: temp_actual
})
);
} catch (error) {
console.error(error.message);
};
});
}).on("error", (error) => {
console.error(error.message);
});
}
});
}
});
});
i really need to return/send/respond the temperature data to my front end but i'm getting said error, is there a different way to return data?
It looks like you are mixing up an HTTP server you wrote in Node (although you haven't shown any relevant code) and an HTTP client you also wrote in Node.
res is an argument received by the callback you pass to http.get and contains data about the response received by your HTTP client.
Meanwhile, somewhere else (not shown) you have a different variable also called res which is the object your HTTP server uses to send its response to the browser running your React code.
You are calling res.send and wanting res to be the latter but it is really the former.
Since you haven't shown us the HTTP server code, it is hard to say where that res is, but there is a good chance you have shadowed it and can solve your problem by using different names (e.g. client_res and server_res).
That said. I strongly recommend avoiding using the http module directly as the API follows out of date design patterns and isn't very friendly. Consider using fetch or axios for making HTTP requests and Express.js for writing HTTP servers.

filled array is empty when send back to user

I try to get data from an azure database with SQL and for this I use tedious. When I execute the code, the array "result" gets filled up with data but when de respond sends the array "result" to the user it is empty. Why and how can I solve it? (I want to send it back in JSON format, the legth is unknown I use 5 to debug).
This is my code
router.get('/', async (req, res) => {
result = []
let rowData = {}
const request = new Request(
`SELECT TOP (5) [LogID]
,[OpdrachtID]
,[ChangeLog]
,[TimeStamp]
,[PersonID]
FROM [log].[LOpdracht]`,
function (err, rowCount, rows) {
if (err) throw err
})
connection.execSql(request)
request.on('row', function(columns){
rowData = {}
columns.forEach(function(column){
rowData[column.metadata.colName] = column.value
})
result.push(rowData)
})
res.send(result)
})
Looks like you are using Tedious. If that is so then you can use the 'done' event to be notified when the request has completed and all rows have been read into result.
request.on('done', () => res.send(result));
Update: As the note in the documentation points out, since you are using execSql you will need to listen to doneProc and doneInProc instead:
request.on('doneProc', () => res.send(result));
request.on('doneInProc', () => res.send(result));
I solved it by adding this:
request.on('requestCompleted', () => {
res.send(result)
})

Node: run 2 sql queries at the same time

I have a simple api to communicate with my mobile app and i have some updates to do.
I want to make 2 updates at the same function (or th same route) but i dont know if its possible.
Here is the dboperation part:
async function updateCusto() {
try {
let pool = await sql.connect(config);
let updateCusto = await pool.request()
.input('input_parameter1', sql.Int, CodOS)
.input('input_parameter2', sql.Int, CodProduto)
.query("update osproduto set custounit=produto.precocusto, valorunitario=produto.precosugerido from OSProduto INNER JOIN Produto ON OSProduto.CodProduto = Produto.Codigo where codproduto=#input_parameter2 and codos=#input_parameter1")
.query("Update OSProduto set sub=qtde*valorunitario where codos=#input_parameter1") //the second one, doenst work
return updateCusto.recordsets;
}
catch (error) {
console.log(error);
throw error;
}
}
and here is the route part:
router.route("/updateCusto").post((request, response) => {
CodOS = request.body.CodOs;
CodProduto = request.body.CodProduto;
dboperations.updateCusto(CodOS, CodProduto).then(result => {
console.log(result);
response.json("Update ok!");
})
.catch(error => response.json({ error }))
})
How can i do this? Is there a way to run the 2 updates on the same operation? Or do i need to create another operation to use on the same route, after the first update is made (and if so, how can i do that?).
It's definitely possible, in fact I would do it as a transaction, this way if one of the queries fails a rollback would be made in order to preserve the state of your database.
Here are my suggestions:
Read about database transactions
Replace pure SQL with an ORM such as Sequelize or KnexJS, it will help you to prevent errors by making queries calling methods such as await OsProduto.update({ where: { id: 0 }}, newData);

My Node Script Hangs after functions are finished

I'm calling three functions, after the completion of these functions I want my script to close on it's own but it just hangs.
I've tried making the functions async/promise based, closing the database after each 'mongodb' type function, and using process.exit() within a function as a callback to the last called function.
Connecting to the (local - not Atlas) Database:
MongoClient.connect(local, {useNewUrlParser: true, useUnifiedTopology: true}, function(err, db) {
if (err) {
console.log(err)
}
else {
console.log('Connected to MongoDB...')
//Read in data from jsonfiles and store each file's contents into the database : This is where the functions are being called... within a successful connect to the MongoDB
insertJSON(db, jsonfiles, 'requests', jsonfilesSource)
insertJSON(db, issuedfiles, 'issuedLicenses', isssuedfilesSource)
insertLicenses(db)
}
db.close()
})
Function 1:
function insertJSON(db, dirBuf,collection, sourceFolder) {
var database = db.db('license-server')
var collection = database.collection(collection)
fs.readdir(dirBuf, function(err, files) {
if (err) {
console.log(err.message)
}
else {
files.forEach(function(filename) {
var text = fs.readFileSync(sourceFolder + filename);
var filecontents = JSON.parse(text)
//collection.insertOne(filecontents)
collection.findOne({"DisplayTitle" : filecontents.DisplayTitle, "NodeInformation" : filecontents.NodeInformation, "Date": filecontents.Date})
.then(function(result) {
if(result) {
console.log(`An Item could already be in the database: A file is unique if its display title, nodeinformation, and date are different.
the items display title is ${result.DisplayTitle}`)
return
}
else {
collection.insertOne(filecontents)
console.log(`Added ${filecontents.DisplayTitle} to database`)
}
})
.catch(function(error) {
console.log(error)
})
})
}
})
}
Function 2:
function insertLicenses(db) {
// Set up GridFS to import .lic and .licx files into the database
var database = db.db('license-server')
var collection = database.collection('fs.files')
var bucket = new mongodb.GridFSBucket(database);
var dirBuf = Buffer.from('../license-server/private/licenses')
fs.readdir(dirBuf, function(err, files) {
if (err) {
console.log(err.message)
}
else {
files.forEach(function(filename) {
collection.findOne({"filename": filename}).
then(function(result) {
if(result) {
console.log(`The file ${filename} is already in the database`)
return
}
else {
fs.createReadStream('./private/licenses/' + filename).
pipe(bucket.openUploadStream(filename)).
on('error', function(error) {
assert.ifError(error)
}).
on('finish', function() {
console.log(`Uploaded ${filename}`)
})
}
})
})
}
})
// I tried calling db.close() here since this is the last function to be called. No luck.
}
I'm guessing it has something to do with the mongodb functions having their own way to close themselves but I couldn't seem to find what I was looking for in previous attempts to resolve this issue.
The expected result should be the script closing itself, the actual result is a handing script.
All of these database calls are asynchronous -- the result of this code running is to immediately call db.close and then do the work in insertJSON and insertLicenses. If you were to rewrite this to use async/await (and you'd need to update your other functions as well) the db.close call would close the db, and that would allow the script to exit:
await insertJSON(db, jsonfiles, 'requests', jsonfilesSource)
await insertJSON(db, issuedfiles, 'issuedLicenses', isssuedfilesSource)
await insertLicenses(db)
db.close()
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Request inside a nested request loop to another server

I need to request X products from another server and I need to wait for that execution to finish before proceeding and saving the order in the database.
Let's say I receive via post an array of product Ids that I need to add to the order, e.g
JSON FILE:
{
"order_products":[1,2,3,4]
}
Here's a code sample:
//Express module
var router = require('express').Router();
//HTTP Request module
var client = require('request');
//Util that saves the URLs of the other databases
var productURL = require('../utils/product/productURL');
//Builds a product object given a JSON
var productBuilder = require('../utils/product/productBuilder');
router.post('/', req, res) {
//Instantiate a new order
var orderInstance = new order({
date: Date.now
});
//Query the products in the other server and add them to the order
req.body.order_products.forEach(id => {
client.get(productURL.HTTPS + id, { json: true }, (err, res, JSONProduct) => {
var product = productBuilder.build(JSONProduct);
orderInstance.order_products.push(product);
});
};
//Save the order in the database
orderInstance.save(....);
//Send response
res.status(201).json(orderInstance);
}
The problem here is that while the loop is still executing, the response is sent (201) and the orderInstance is saved without any product. If I console.log the products they only appear after the orderInstance is saved.
I've tried implementing callbacks to fix this issue, but with no success. I'd appreciate if anyone could lend me a hand here! Thanks in advance :smiley:(edited)
forEach runs synchronously - when the forEach ends, the client.get requests may have all been sent out, but the responses surely haven't come back yet. You need to convert each request into a Promise, and then call Promise.all on an array of those Promises. The Promise.all will resolve once all responses have come back. For example:
const allPromises = req.body.order_products.map(id => new Promise((resolve, reject) => {
client.get('productURL.HTTPS' + id, { json: true }, (err, res, JSONProduct) => {
if (err) reject (err);
else resolve(productBuilder.build(JSONProduct));
});
}));
Promise.all(allPromises)
.then((newProducts) => {
orderInstance.order_products.push(...newProducts);
res.status(201).json(orderInstance);
})
.catch((err) => {
// handle errors
});

Categories

Resources