Make my db queries synchronous with with my promise based function - node.js

How can I make my db queries synchronous with with my promise base function? In my code, I am running 3 db operations inside 3 different functions which need to be run in order, like waterfall model. The functions are running in waterfall model but db queries inside those functions are working in asynchronous. I need to run db queries inside those function in synchronous.
In this example I am expecting in console:
1
2
3
4
But I am getting
1
3
2
4
Code:
const Promise = require('bluebird');
// DB Settings
const dbConfig = {
user: process.env.DBUSER,
password: process.env.DBPWD,
database: process.env.DBNAME,
host: process.env.DBHOST,
port: process.env.DBPORT,
poolSize: 10, // max number of clients in the pool
//poolIdleTimeout: 30000, // how long a client is allowed to remain idle before being closed
//reapIntervalMillis: 1000 //frequency to check for idle clients within the client pool
};
const pgp = require('pg-promise')();
const db = pgp(dbConfig);
var currentStatus = '',newStatus = '';
const _updateCurrentStatus = () => new Promise((resolve, reject) => {
const _getCurrentStatus = (_userId) => new Promise((_resolve, _reject) => {
console.log("1");
let statusQuery = "SELECT status FROM users WHERE id=" + _userId;
db.one(statusQuery).then(function (data) {
console.log("2");
currentStatus = data.status;
_resolve();
}).catch(function (error) {
_reject(error);
});
});
const _setUpdateStatus = (cStatus, nStatus) => new Promise((_resolve, _reject) => {
if(allApproved){
if(cStatus == 'nvd_pending'){
//nStatus = 'finance_pending';
newStatus = 'finance_pending';
}else if(cStatus == 'finance_pending'){
//nStatus = 'oracle_pending';
newStatus = 'oracle_pending';
}else if(cStatus == 'oracle_pending'){
//nStatus = 'active';
newStatus = 'active';
}else{
//nStatus = cStatus;
newStatus = cStatus;
}
}else{
//nStatus = 'nvd_pending';
newStatus = 'nvd_pending';
}
//_resolve(nStatus);
_resolve();
});
const _updateStatus = (_newStatus, _payLoad) => new Promise((_resolve, _reject) => {
console.log("3");
let updateuserQuery = "UPDATE users SET status = '"+ _newStatus + "' WHERE id=" + _payLoad.user_id;
let updatePanQuery = "UPDATE user_documents SET status = '" + _payLoad.panstatus + "' WHERE id= " + _payLoad.panid + " AND user_id=" + _payLoad.user_id;
let updateFinanceQuery = "UPDATE user_finance_details SET status = '" + _payLoad.financestatus +" 'WHERE id= " + _payLoad.financeid + " AND user_id=" + _payLoad.user_id;
db.tx(function (t) {
console.log("4");
// `t` and `this` here are the same;
// this.ctx = transaction config + state context;
return t.batch([
t.none(updateuserQuery),
t.none(updatePanQuery),
t.none(updateFinanceQuery)
]);
}).then(function (data) {
_resolve(data);
}).catch(function (error) {
_reject(error);
});
});
_getCurrentStatus(payLoad.user_id)
.then(_setUpdateStatus)
.then( _updateStatus(newStatus, payLoad))
.then( values => {
resolve(values);
},error => {
reject(error);
})
.catch((error) => reject(error));
});

You are overcomplicating thing here. First feedback is that you don't need those new Promise wrappers, since pg-promise is already creating promises. You can greatly flatten things here:
function getCurrentStatus(userId) {
console.log("1");
let statusQuery = "SELECT status FROM users WHERE id=" + userId;
return db.one(statusQuery).then(function (data) {
console.log("2");
return data.status;
});
}
function getUpdatedStatus(cStatus)
console.log('2');
if (allApproved) {
if(cStatus == 'nvd_pending'){
newStatus = 'finance_pending';
} else if (cStatus == 'finance_pending'){
newStatus = 'oracle_pending';
} else if (cStatus == 'oracle_pending'){
newStatus = 'active';
} else {
newStatus = cStatus;
}
} else {
newStatus = 'nvd_pending';
}
return newStatus;
}
function updateStatus(newStatus, payLoad) {
console.log("3");
let updateuserQuery = "UPDATE users SET status = '"+ newStatus + "' WHERE id=" + payLoad.user_id;
let updatePanQuery = "UPDATE user_documents SET status = '" + payLoad.panstatus + "' WHERE id= " + payLoad.panid + " AND user_id=" + payLoad.user_id;
let updateFinanceQuery = "UPDATE user_finance_details SET status = '" + payLoad.financestatus +" 'WHERE id= " + payLoad.financeid + " AND user_id=" + payLoad.user_id;
return db.tx(function (t) {
console.log("4");
// `t` and `this` here are the same;
// this.ctx = transaction config + state context;
return t.batch([
t.none(updateuserQuery),
t.none(updatePanQuery),
t.none(updateFinanceQuery)
]);
});
});
function updateCurrentStatus(payLoad) {
return getCurrentStatus(payLoad.user_id)
.then(cStatus => getUpdatedStatus(cStatus))
.then(newStatus => updateStatus(newStatus, payLoad));
}
The specific reason you're seeing 3 out of order is because you're calling it immediately via _updateStatus(newStatus, payLoad) instead of wrapping it in a function (see my suggested code updates above).

Related

Communication between NodeJS and webpage

I built simple weather webpage basing on the tutorial. and I have a webpage with below JS code;
I have also Node JS code( on the bottom) for temperature and pressure sensor written with Johnny-Five. This works perfectly but what I wanna do is display readouts from the sensors setup with NodeJS on the webpage. What is the best approach to do that? I tried to setup this with websocket but it was not really working.
let weather = {
apiKey: "xxxxxxxxxxxxxx",
fetchWeather: function (city) {
fetch(
"https://api.openweathermap.org/data/2.5/weather?q=" + city + "&units=metric&appid=" + this.apiKey
).then((response) => {
return response.json();
})
.then((data) => this.displayWeather(data));
},
displayWeather: function (data) {
const { name } = data;
const { icon, description } = data.weather[0];
const { temp, humidity, pressure } = data.main;
const { speed } = data.wind;
const temperatura = Math.round(temp);
document.querySelector(".miasto").innerText = "Pogoda w " + name;
document.querySelector(".icon").src =
"https://openweathermap.org/img/wn/" + icon + ".png";
document.querySelector(".opis").innerText = description;
document.querySelector(".temperatura").innerText = temperatura + "°C";
document.querySelector(".wilgotnosc").innerText =
"Wilgotność: " + humidity + "%";
document.querySelector(".cisnienie").innerText =
"Ciśnienie: " + pressure + "HPa";
document.querySelector(".wiatr").innerText =
"Prędkość wiatru: " + speed + " km/h";
document.querySelector(".pogoda").classList.remove("loading");
document.body.style.backgroundImage =
"url('https://source.unsplash.com/1600x900/?" + name + "')";
},
search: function () {
this.fetchWeather(document.querySelector(".search-bar").value);
},
};
document.querySelector(".search button").addEventListener("click", function () {
weather.search();
});
document
.querySelector(".search-bar")
.addEventListener("keyup", function (event) {
if (event.key == "Enter") {
weather.search();
}
});
weather.fetchWeather("Katowice");
var five = require("johnny-five");
var board = new five.Board(
{
port: "COM4"
}
);
board.on("ready", function() {
var multi = new five.Multi({
controller: "BMP180",
freq: 500
});
multi.on("change", function() {
console.log("Termometr");
console.log(" Temperatura : ", this.thermometer.celsius, " ℃");
console.log("--------------------------------------");
var pressure2 = Math.round(this.barometer.pressure*100, 1)/10;
console.log("Barometr");
console.log(" Ciśnienie : ", pressure2, " HPa" );
console.log("--------------------------------------");
});
});
I managed to solve the issue. Quite simple but mybe it will help someone here
Data which I am sending are
const WebSocket = require("ws");
const wss = new WebSocket.Server({ port: 8085 })
var obj = {
press: "69",
temp: "24"
}
wss.on("connection", ws => {
ws.send(JSON.stringify(obj));
} );
And I get it:
const ws = new WebSocket("ws://localhost:8085");
ws.addEventListener("open", () => {
console.log("We are connected");
ws.addEventListener("message", (data) => {
console.log(JSON.parse(data.data));
});
});

Code runs before request is completed on NodeJS

I have the following code: (the code runs without issues)
function getValoresAcao(acoes) {
acoes.forEach(acao => {
getValorAcao(acao)
});
console.log("the end")
}
function getValorAcao(acao) {
url = 'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&parse_mode=HTML&symbol='+acao+'&apikey='+API_KEY
request(url, { json: true }, (err, res, body) => {
if (err) { return console.log(err); }
let contador = 0;
let valorDeDoisDias = [];
const dailyJsonObject = body["Time Series (Daily)"]
var objectKeysArray = Object.keys(dailyJsonObject)
objectKeysArray.forEach(function(objKey) {
var objValue = dailyJsonObject[objKey]
if (contador < NUMERO_DE_ELEMENTOS )
{
//console.log(dailyJsonObject);
const valorFechamento = objValue["4. close"]
console.log(valorFechamento);
contador = contador + 1
valorDeDoisDias.push(valorFechamento);
}
});
const textoDiferenca = getDiferencaEmPorcentagem(valorDeDoisDias);
let bigString = "" + acao + " | " + valorDeDoisDias[0] + " | " + textoDiferenca
request(urlTelegram + bigString, { json: true }, (err, res, bodyTelegram) => {
console.log(bodyTelegram);
})
});
}
function getDiferencaEmPorcentagem(valprDeDoisDias) {
let myString = ""
const valorDiaAnterior = valprDeDoisDias[0]
const valorDiaMaisRecente = valprDeDoisDias[1]
myString = (valorDiaAnterior - valorDiaMaisRecente ) / valorDiaMaisRecente * 100
myString = myString.toFixed(2)
console.log(myString)
return myString + "%"
}
But the code console.log("the end") is supposed to run after the request is completed, but it runs when I start the code, it didn't wait the request to be finished.
How can I make the "the end" part of the code wait the request be executed?
probably something like that could help:
async function getValoresAcao(acoes) {
await Promise.all(acoes.map(getValorAcao))
console.log("the end")
}
async function getValorAcao(acao) {
const url = 'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&parse_mode=HTML&symbol=' + acao + '&apikey=' + API_KEY
return new Promise ((resolve, reject)=>{
try{
request(url, { json: true }, (err, res, body) => {
if (err) {
throw err
}
let contador = 0;
let valorDeDoisDias = [];
const dailyJsonObject = body["Time Series (Daily)"]
var objectKeysArray = Object.keys(dailyJsonObject)
objectKeysArray.forEach(function (objKey) {
var objValue = dailyJsonObject[objKey]
if (contador < NUMERO_DE_ELEMENTOS) {
//console.log(dailyJsonObject);
const valorFechamento = objValue["4. close"]
console.log(valorFechamento);
contador = contador + 1
valorDeDoisDias.push(valorFechamento);
}
});
const textoDiferenca = getDiferencaEmPorcentagem(valorDeDoisDias);
let bigString = "" + acao + " | " + valorDeDoisDias[0] + " | " + textoDiferenca
request(urlTelegram + bigString, { json: true }, (err, res, bodyTelegram) => {
if(err){
throw err
}
resolve(bodyTelegram)
})
});
} catch (e) {
reject(e)
}
})
}
function getDiferencaEmPorcentagem(valprDeDoisDias) {
let myString = ""
const valorDiaAnterior = valprDeDoisDias[0]
const valorDiaMaisRecente = valprDeDoisDias[1]
myString = (valorDiaAnterior - valorDiaMaisRecente) / valorDiaMaisRecente * 100
myString = myString.toFixed(2)
console.log(myString)
return myString + "%"
}

Node Api MongoDB updating date as string

I have a Node API communicating with MongoDB install. The MongoDB node api ("mongodb": "^3.5.9") insists on converting date objects into String before updating to the db. Can anybody please help?
app.post("/api/updateDocument",auth,(req,res) => {
const collection = req.body.Collection;
incomingmap = req.body.inComingMap
const searchval = req.body.searchVal;
const searchvar = req.body.searchVar;
let findargs = {}
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://...";
MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true },function(err,db) {
if (err) throw err;
var dbo = db.db();
var coll = dbo.collection(collection);
coll.find({}).toArray(function(err,data) {
data.forEach((item) => {
if (item[searchvar] == searchval) {
for (let key in incomingmap) {
value=incomingmap[key]
console.log(key + " : " + value);
try {
da=Date.parse(value);
//incomingmap[key]=da.toIs;
coll.updateOne({_id:item._id},{$set: {key:da}},function() {})
} catch{
//coll.updateOne({_id:item._id},{$set: {key: value}},function() {})
}
}
return res.json({message:searchval+' updated.'})
} else {
setTimeout(() => {
res.end(`${searchval} could not be found.`)
}, 1000)
}
})
})
})
})
Expected Result
"calibration" : ISODate("2021-11-07T11:23:01.306Z"),
Actual Result
"calibration" : "2021-11-07T01:00:00.000Z",
I wrote a new function to parse the dates and store them back into the inComingMap
for (let key in incomingmap) {
value=incomingmap[key]
console.log(key + ": " + value);
try
{
da=new Date(value);
incomingmap[key]=da;
console.log(key + " = " + value +" is a date "+da);
map[key]=da
}
catch {
map[key]=value
console.log("K"+key + " V" + value );
}
}

How do I test an agenda job with jest?

I want to test the jobs I'm running with agenda. I have written the following test but then I realized that all the expect()... functions are not getting called before it determines that the test had passed or failed. I put some console.log() by all the expect() functions and they do get called just not before it determines if the tests result. I tried adding expect.assertions(6); at the top of the test but it didn't make the test wait longer, it just failed saying it didn't find all 6.
How do I make jest wait for the whole test to finish?
This is the code I want to test:
rules.js
const Account = require("./../models/user.account");
const Listing = require("./../models/user.account.listing");
const Message = require("./../models/user.account.listing.message");
const MessageRule = require("./../models/user.account.listing.messageRule");
const Reservation = require("./../models/user.account.listing.reservation");
const Core = require("./../core");
const moment = require("moment-timezone");
require("colors");
module.exports = function(agenda) {
agenda.define("send message", {priority: "highest"}, async (job, done) => {
try {
console.time("send message " + job.attrs._id);
const userID = job.attrs.data.userID;
const listingID = job.attrs.data.listingID;
const reservationID = job.attrs.data.reservationID;
const messageRuleID = job.attrs.data.messageRuleID;
const sendDate = job.attrs.data.sendDate;
//console.log("Rule Job:".cyan + " send message - listingID: " + listingID);
let messageQuery = {
userID,
listingID,
reservationID,
messageRuleID
};
let message = await Message.findOne(messageQuery);
if (message) {
throw new Error(
"Message has already been sent. userID: " +
userID +
" listingID: " +
listingID +
" reservationID: " +
reservationID +
" messageRuleID: " +
messageRuleID +
" message._id: " +
message._id +
" message.sendDate: " +
message.sendDate +
" message.sentDate: " +
message.sentDate
);
} else {
const isLastMinuteMessage = false;
let listing = await Listing.findById(listingID);
let account = await Account.findById(listing.accountID);
let messageRule = await MessageRule.findById(messageRuleID);
let reservation = await Reservation.findById(reservationID);
// Check that it found all of the required documents
if (!listing || !listing._id) {
throw new Error("Missing the listing document. userID: " + userID + " listingID: " + listingID);
}
if (!account || !account._id) {
throw new Error(
"Missing the account document. userID: " + userID + " accountID: " + listing.accountID
);
}
if (!messageRule || !messageRule._id) {
throw new Error(
"Missing the messageRule document. userID: " + userID + " messageRuleID: " + messageRuleID
);
}
if (!reservation || !reservation._id) {
throw new Error(
"Missing the reservation document. userID: " + userID + " reservationID: " + reservationID
);
}
// Double check the send date by recalculating it then checking comparing them
if (messageRule.event == "checkin" || messageRule.event == "checkout") {
let sendDateCheck = moment.tz(
reservation.startDate + " " + messageRule.time,
"YYYY-MM-DD H",
listing.timeZone
);
if (messageRule.event == "checkout") {
sendDateCheck = sendDateCheck.add(reservation.nights, "day");
}
sendDateCheck = sendDateCheck.add(messageRule.days, "day");
if (!sendDateCheck.isSame(sendDate)) {
throw new Error(
"Message send date and calculated send date don't match. userID: " +
userID +
" listingID: " +
listingID +
" reservationID: " +
reservationID +
" messageRuleID: " +
messageRuleID +
" sendDate: " +
moment(sendDate).format() +
" sendDateCheck: " +
moment(sendDateCheck).format()
);
}
}
await Core.buildMessage(account, listing, messageRule, reservation, isLastMinuteMessage);
}
console.timeEnd("send message " + job.attrs._id);
done();
} catch (error) {
console.error("Rule Job: send message", error);
return done(error);
}
});
};
And here is my test:
rules.test.js
const Account = require("./../models/user.account");
const Listing = require("./../models/user.account.listing");
const Message = require("./../models/user.account.listing.message");
const MessageRule = require("./../models/user.account.listing.messageRule");
const Reservation = require("./../models/user.account.listing.reservation");
const Core = require("./../core");
const Rules = require("./rules");
const Docs = require("./../tests/docs");
describe("Tests for rules.js", () => {
// Mock moment()
Date.now = jest.fn(() => 1538794832371); //October 5th 2018 8pm PST
beforeEach(() => {
jest.clearAllMocks();
});
describe("rules()", () => {
it("should send a message, message exists but is enabled", async () => {
// Mock Message
// Message.findOne()
const MessageFindOneMock = jest.spyOn(Message, "findOne");
const MessageFindOneResult = Docs.messageReviewReminderDisabledThenEnabled;
const MessageFindOne = jest.fn(() => MessageFindOneResult);
MessageFindOneMock.mockImplementation(MessageFindOne);
const userID = Docs.userID;
const reservation = Docs.reservationInTheFuture;
const messageRule = Docs.messageRuleCheckUp;
const accountID = Docs.listing.accountID;
const listingID = Docs.listing._id;
const reservationID = reservation._id;
const messageRuleID = messageRule._id;
// Mock Listing
// Listing.findById()
const ListingFindByIdMock = jest.spyOn(Listing, "findById");
const ListingFindByIdResult = Docs.listing;
const ListingFindById = jest.fn(() => ListingFindByIdResult);
ListingFindByIdMock.mockImplementation(ListingFindById);
// Mock Account
// Account.findById()
const AccountFindByIdMock = jest.spyOn(Account, "findById");
const AccountFindByIdResult = {_id: accountID};
const AccountFindById = jest.fn(() => AccountFindByIdResult);
AccountFindByIdMock.mockImplementation(AccountFindById);
// Mock MessageRule
// MessageRule.findById()
const MessageRuleFindByIdMock = jest.spyOn(MessageRule, "findById");
const MessageRuleFindByIdResult = messageRule;
const MessageRuleFindById = jest.fn(() => MessageRuleFindByIdResult);
MessageRuleFindByIdMock.mockImplementation(MessageRuleFindById);
// Mock Reservation
// Reservation.findById()
const ReservationFindByIdMock = jest.spyOn(Reservation, "findById");
const ReservationFindByIdResult = reservation;
const ReservationFindById = jest.fn(() => ReservationFindByIdResult);
ReservationFindByIdMock.mockImplementation(ReservationFindById);
// Mock Core
// Core.buildMessage()
const CoreBuildMessageMock = jest.spyOn(Core, "buildMessage");
const CoreBuildMessage = jest.fn((account, listing, messageRule, reservation, isLastMinuteMessage) => {
expect(account._id).toBe(accountID);
expect(listing._id).toBe(listingID);
expect(messageRule._id).toBe(messageRuleID);
expect(reservation._id).toBe(reservationID);
});
CoreBuildMessageMock.mockImplementation(CoreBuildMessage);
// Run test
const sendDate = "2018-11-08T10:00:00-06:00";
const done = jest.fn(error => {
expect(error).toBeFalsy();
expect(CoreBuildMessage).toHaveBeenCalledTimes(10);
});
const job = {
attrs: {
data: {
userID,
listingID,
reservationID,
messageRuleID,
sendDate
}
}
};
let agenda = {
define: jest.fn((name, options, callback) => {
if (name == "send message") {
callback(job, done);
}
})
};
Rules(agenda);
});
});
});
In your rules.js you have defined your callback as an async function:
agenda.define("send message", {priority: "highest"}, async (job, done) // <--- this
However you are calling it with:
define: jest.fn((name, options, callback) => {
if (name == "send message") {
callback(job, done);
}
})
So there is no await there ...
Which explains why you get stuff running but not awaiting for it to finish.
Should be something like:
define: await jest.fn((name, options, callback) => {
if (name == "send message") {
await callback(job, done);
}
})

Getting Response of a function defined in another file in node.js

I have a socket function defined as
var funcs = require('./funcs');
socket.on(EVENT_ACCEPT_ORDER, function(data, callback)
{
data = JSON.parse(data);
var bookingId = data.bookingId;
var userId = data.userId;
console.log("Accepting booking...." + bookingId);
var query = "UPDATE bookings SET bookingStatus = " + BOOKING_STATUS_ACCEPTED + " WHERE id = " + bookingId + " AND bookingStatus = " + BOOKING_STATUS_IN_QUEUE;
con.query(query, function(err, rows,fields)
{
if(err)
{
console.log("mysql query error");
}
else
{
if(rows.changedRows > 0)
{
var indexOfUser = usersList.indexOf(userId);
if(indexOfUser > -1)
{
userSockets[indexOfUser].emit(EVENT_USER_ORDER_ACCEPTED);
}
callback({"message": "Success","error":false, "booking": funcs.getBooking(con, bookingId)});
}
else
callback({"message": "Success","error":true});
}
});
});
Funcs is defined as
module.exports = {
"getBooking": function (con, bookingId)
{
var query = "SELECT * FROM bookings WHERE id = " + bookingId + " LIMIT 1";
con.query(query, function(err, rows,fields)
{
if(err)
{
console.log("mysql query error");
}
else if (rows.length == 1)
{
var booking = rows[0];
var userId = rows[0]['userId'];
var query = "SELECT id, name, phone, imagePath FROM users WHERE id = " + userId + " LIMIT 1";
con.query(query, function(err, rows,fields)
{
if(err)
{
console.log("mysql query error");
}
else if (rows.length == 1)
{
booking['user'] = rows[0];
return booking;
}
});
}
});
}
}
Everything is running fine except
callback({"message": "Success","error":false, "booking": funcs.getBooking(con, bookingId)});
in this function instead of booking, i am only getting
{"error":false,"message":"Success"}
Why am i not getting the booking function result?
You are not getting the result, because the result of the callback function in con.query is not returned to the caller of getBooking. It is the asynchronous pattern, which you are not processing correctly.
The way it is supposed to work is that the getBooking gets an extra argument: a function to be called when data are available (in an internal asynchronous call to con.query). Such a function is then provided by the caller and in this function you do whatever you want with the data:
funcs.js
"getBooking": function (con, bookingId, callback) {
...
con.query(query, function(err, rows,fields) {
...
// instead of return booking do
callback(err, booking);
...
}
}
main module
// instead of
callback({"message": "Success","error":false, "booking": funcs.getBooking(con, bookingId)});
// do
funcs.getBooking(con, bookingId, function(err, booking) {
callback({"message": "Success","error":false, "booking": booking});
});
I am afraid this is not the only issue in your code, but this should be the first to fix. Read further about processing asynchronous calls in general and specifically in node.js and fix other places in your code correspondingly.

Resources