Unable to run a function synchronously in nodejs and express - node.js

I have used wikipedia-js for this project. This is my code for summary.js file.
var wikipedia = require("wikipedia-js");
var something = "initial";
module.exports = {
wikitext: function(topicname) {
console.log("Inside wikitex funciton :" + topicname);
var options = {
query: topicname,
format: "html",
summaryOnly: false,
lang: "en"
};
wikipedia.searchArticle(options, function(err, htmlWikiText) {
console.log("Inside seararticlefunciton :");
if (err) {
console.log("An error occurred[query=%s, error=%s]", topicname, err);
return;
}
console.log("Query successful[query=%s, html-formatted-wiki-text=%s]", topicname, htmlWikiText);
something = htmlWikiText;
});
return something;
},
};
This module I am using in /wiki/:topicname route. The corresponding code in index.js is like this.
router.get('/wiki/:topicname', function(req, res, next) {
var topicname = req.params.topicname;
console.log(topicname);
var first = summary.wikitext(topicname);
res.send("Hello "+first);
});
The problem is, everytime i visit a wiki/some-topic, the last return statement of summary.js executes before htmlWikiText is populated with content. So I always see hello initial on the browser page. Although after sometime it gets printed on terminal due to console.log statement.
So how should I resolve this issue?

I'm not going to try turning this code into synchronous. I'll just correct it to work as an asynchronous version.
You need to pass in callback to wikitext() and return the value in that callback. Here is the revised code of wikitext() and the route that calls it:
var wikipedia = require("wikipedia-js");
module.exports = {
wikitext: function(topicname, callback) {
console.log("Inside wikitex funciton :" + topicname);
var options = {
query: topicname,
format: "html",
summaryOnly: false,
lang: "en"
};
wikipedia.searchArticle(options, function(err, htmlWikiText) {
console.log("Inside seararticlefunciton :");
if (err) {
console.log("An error occurred[query=%s, error=%s]", topicname, err);
return callback(err);
}
console.log("Query successful[query=%s, html-formatted-wiki-text=%s]", topicname, htmlWikiText);
callback(null, htmlWikiText);
});
}
};
router.get('/wiki/:topicname', function(req, res, next) {
var topicname = req.params.topicname;
console.log(topicname);
summary.wikitext(topicname, function(err, result) {
if (err) {
return res.send(err);
}
if (!result) {
return res.send('No article found');
}
res.send("Hello "+result);
});
});

Related

How to return value from a file to router layer using nodejs and expressjs

i have above api in TestRouter.js
TestRouter.js
router.get('/all', function(req, resp) {
var data = reportBo.getAll();
console.log(data);
resp.status(200);
resp.send(data);
return resp;
});
i am calling getAll() from TestRouter.js to TestDao.js.
it is working fine and can fetch the data and can print in console. but i am trying to send this result to TestRouter.js and i am trying to print it on console. but it is showing undefined.
TestDao.js
module.exports.getAll = function () {
var connection = myDB.get();
connection.collection('REPORTS').find({}).toArray(function (err, result) {
if (err) {
throw err;
} else {
//console.log(result);
return result;
}
});
};
module.exports.getAll = function (callback) {
var connection = myDB.get();
connection.collection('REPORTS').find({}).toArray(function (err, result) {
if (err) {
callback(err);
} else {
//console.log(result);
callback(null, result);
}
});
};
And in your router:
router.get('/all', function(req, resp) {
reportBo.getAll(function(err, data){
if(err){
resp.status(500);
} else {
resp.status(200);
resp.send(data);
}
});
});
This way of doing things with callbacks is quite common in Node JS. Also, there is a better way called Promises. You can read up on it.

Mocha with chai and supertest: expected undefined to equal

I wrote the unit tests:
var app = require('../server');
var chai = require('chai');
var supertest = require("supertest")(app);
var GoogleUrl = require('google-url');
var config = require('../config');
var expect = chai.expect;
describe('Urls Tests', function () {
var url = {
author : 'Alexey',
description : 'grrggr',
full_url : 'https://github.com',
date : '30-06-2017',
time : '18:21:27',
count_click : 0,
list_tags : [
'Sport',
'Football'
]
};
var token;
beforeEach(function (done) {
agent
.post('http://localhost:8000/auth/login')
.send({email: 'Keane95#yandex.ru', password: '123456'})
.end(function (err, res) {
if (err) {
return done(err);
}
expect(res.body.userData).to.have.property('token');
token = res.body.userData.token;
done();
});
});
it('should create a url', function(done) {
var googleUrl = new GoogleUrl({
'key': config.get('google_key')
});
googleUrl.shorten(url.full_url, function (err, shortUrl) {
url.short_url = shortUrl;
supertest
.post('/urls/create')
.send(url)
.expect(401)
.end(function (err, res) {
if (err) return done(err);
expect(res.body.author).to.equal('Alexey');
url = res.body;
done();
});
});
});
it('should modify a url by id', function(done) {
url.description = 'Good description';
url.list_tags.push('Liverpool');
supertest
.put('/urls/' + url._id)
.send(url)
.expect(401)
.end(function(err, res) {
if (err) return done(err);
expect(res.body.description).to.equal('Good description');
expect(res.body.list_tags[2]).to.equal('Liverpool');
done();
});
});
it('should modify a count of clicks', function(done) {
url.count_click++;
supertest
.put('/urls/' + url._id)
.send(url)
.expect(401)
.end(function(err, res) {
if (err) return done(err);
expect(res.body).to.equal('Count of the click is updated');
done();
});
});
});
I run to execute the unit tests and get the errors:
I read the articles by unit tests.
First article: http://developmentnow.com/2015/02/05/make-your-node-js-api-bulletproof-how-to-test-with-mocha-chai-and-supertest/
Second article: https://www.codementor.io/olatundegaruba/integration-testing-supertest-mocha-chai-6zbh6sefz
I don't understand why I get these errors. Please, help me. I think that I made little error, but since I cannot fint it.
UPDATED
I added route:
var express = require('express');
var GoogleUrl = require('google-url');
var _ = require('lodash');
var token = require('../middlewares/token');
var Url = require('../models/url');
var config = require('../config');
var router = express();
router.post('/create', token.required, createShortUrl);
router.put('/count/:id', token.required, updateCountClick);
router.put('/:id', token.required, updateUrlById);
module.exports = router;
function createShortUrl(req, res) {
_.trim(req.body.list_tags);
var tags = _.split(req.body.list_tags, ',');
tags.splice(tags.length - 1, 1);
var date = returnDate();
var time = returnTime();
var googleUrl = new GoogleUrl({
'key': config.get('google_key')
});
googleUrl.shorten(req.body.full_url, function (err, shortUrl) {
if (err) {
res.status(500).json(err);
}
var url = new Url({
'author': req.payload.username,
'description': req.body.description,
'full_url': req.body.full_url,
'short_url': shortUrl,
'list_tags': tags,
'date': date,
'time': time
});
url.save(function (err, url) {
if (err) {
return res.status(500).json(err);
} else {
return res.status(200).json(url);
}
});
});
}
function updateCountClick(req, res) {
var count_click = req.body.count_click + 1;
Url.findOneAndUpdate({_id: req.params.id}, {$set: {count_click: count_click}}, {new: true}, function (err, url) {
if (err) {
return res.status(500).json(err);
}
if (url) {
return res.status(200).json('Count of the click is updated');
}
});
}
function updateUrlById(req, res) {
_.trim(req.body.list_tags);
var tags = _.split(req.body.list_tags, ',');
tags.splice(tags.length - 1, 1);
Url.findOneAndUpdate({_id: req.params.id}, {$set: {description: req.body.description, list_tags: tags}}, {new: true}, function (err, url) {
if (err) {
res.status(500).json(err);
}
if (url) {
res.status(200).json(url);
}
});
}
UPDATED 2
Authoziration was added:
var token;
beforeEach(function (done) {
agent
.post('http://localhost:8000/auth/login')
.send({email: 'Keane95#yandex.ru', password: '123456'})
.end(function (err, res) {
if (err) {
return done(err);
}
expect(res.body.userData).to.have.property('token');
token = res.body.userData.token;
done();
});
});
Also I updated code my unit-tests.
I can't see where in your code you send 401 and Url. So it seems that your test requests are getting rejected by token.required middleware with 401 status code (which means "unauthorized").
.send(url)
.expect(401) // why do you expect 401? You never send it inside your logic
So basically your test never hit actual code.
First of all, you do need to fake authorization to make token.required middleware happy.
Then expect 200 result
.send(url)
.expect(200) // normal execution flow of createShortUrl results in 200
.end(/* rest of your test logic */)

Error handling in async.parallel

I have a function which uses async.parallel function to call two functions. But I don't know how to handle errors when async.parallel is used. If one of the two functions throw errors, how do I handle them?
exports.getProductconfigWithProductList = function(req, res){
var apiVersion = req.param('version', null);
var product_id = req.params.productId;
dbDriver = determineDriver('es');
async.parallel([
function(callback) {
dbDriver.getProductconfig(product_id,function(data){
callback(null, data);
});
},
function(callback) {
var productListPromise = getProductListData();
productListPromise.then(function(data) {
callback(null, data);
});
}
],
function(err, results) {
if(!err){
var data = results[0];
data[1] = results[1];
res.send(data);
res.end();
} else {
console.log('<<<<<<<<<<<<<<'+err+'>>>>>>>>>>>>>>>');
res.send(err);
res.end();
}
}
);
};
When you have:
async.parallel([
func1,
func2,
], (err, data) => {
if (err) {
// you handle the errors here
}
});
It's explained in more detail in the docs:
https://caolan.github.io/async/docs.html
and in the issue on GitHub here:
https://github.com/caolan/async/issues/334

Node/Express function and callback are not breaking with return

I am creating a 'refresh data' function in Node and I cannot figure out where to place the callbacks and returns. The function continues to run. Below is a list of things the function should do. Could someone help out?
Check if a user has an api id in the local MongoDB
Call REST api with POST to receive token
Store token results in a MongoDB
Terminate function
./routes/index.js
router.post('/refresh', function(req, res) {
var refresh = require('../api/refresh');
refresh(req, function() { return console.log('Done'); });
});
../api/refresh.js
var callToken = require('./calltoken');
var User = require('../models/user'); // Mongoose Schema
module.exports = function(req, callback) {
User.findOne( {'username':req.body.username}, function(err, user) {
if(err) { console.log(err) }
if (user.api_id == 0) {
callToken.postToken(req.body.username, callback);
} else { // Do something else }
});
};
./calltoken.js
var request = require('request');
var Token = require('../models/token'); // Mongoose Schema
module.exports = {
postToken: function(user, callback) {
var send = {method:'POST', url:'address', formData:{name:user} };
request(send, function(err, res, body) {
if(err) { console.log(err) }
if (res.statusCode == 201) {
var newToken = new Token();
newToken.token = JSON.parse(body).access_token['token'];
newToken.save(function(err) {
if(err) { console.log(err) }
return callback();
});
}
});
}
};
I'm not an expert in Express but everywhere in you code in lines with if(err) { console.log(err) } you should stop execution (maybe of course not - up to you app) and return 400 or 500 to client. So it can be something like
if(err) {
console.log(err);
return callback(err); // NOTICE return here
}
On successful execution you should call return callback(null, result). Notice null as a first argument - it is according nodejs convention (error always goes as first argument).

How to pass changes from middleware to socket.io?

I am using node.js with socket.io to push real time notifications to users. However, currently I am just sending back a query result done in my socket.io code and sending it back to the client but I need to let socket know about the changes that occur and to either update with the changes or re-query the db to check for the new number and send that to the client.
For example if a user gets a friend request then the notification count will change and I want socket.io to push the new notification count number to the user.
here is my socket.io code in my app.js file:
io.on('connection', function(socket) {
var sessionID = socket.handshake.sessionID,
session = new connect.middleware.session.Session({ sessionStore: sessionStore }, socket.handshake.session)
console.log('socket: new ' + sessionID)
socket.broadcast.emit('arpNewConn', session.passport.user)
var intervalID = setInterval(function() {
socket.handshake.session.reload(function() {
socket.handshake.session.touch().save()
})
socket.emit('pulse', { heartbeat: new Date().toString(), timestamp: new Date().getTime() })
}, 300 * 1000)
socket.on('disconnect', function() {
console.log('socket: dump ' + sessionID)
socket.broadcast.emit('arpLostConn', session.passport.user)
clearInterval(intervalID)
})
socket.emit('entrance', {message: 'Message works'});
dbnotif.findOne(userID, function (err, user) {
if(err) throw err;
notify = user.notifications;
socket.emit('notify', {notific: notify});
});
});
Here is the client side:
div#CheckSocket
script(src='http://localhost:3000/socket.io/socket.io.js')
script.
$(document).ready(function () {
console.log('socket');
var socket = io.connect('http://localhost:3000/');
console.log('entered1');
socket.on('entrance', function (data) {
console.log('entered');
console.log(data.message);
});
socket.on('notify', function (data) {
console.log('noting');
console.log(data.notific);
if(data.notific !== 0)
$('.notifications').html(data.notific);
});
socket.on('reconnecting', function(data) {
setStatus('reconnecting');
console.log('entered2');
});
function setStatus(msg) {
console.log('connection status: ' + msg);
console.log('entered5');
}
});
Here is the example of adding a friend in the route file:
exports.addContactPost = function(req, res, err) {
async.waterfall([
function(callback) {
var success;
var newFriend = new Friend ({
userId: req.signedCookies.userid,
friend_id: mongoose.Types.ObjectId(req.body.otherUser),
friend_status: 1
});
newFriend.save(function(err){
if(err) {
console.log(err);
} else {
console.log("saved it");
success = true;
}
});
callback(null, success)
},
function(success, callback) {
//if(success === true) {
var success2;
var newFriend2 = new Friend ({
userId: mongoose.Types.ObjectId(req.body.otherUser),
friend_id: req.signedCookies.userid,
friend_status: 2
});
newFriend2.save(function(err){
if(err) {
res.send("request not received");
} else {
success2 = true;
}
});
callback(null, success2);
//} else {
// res.send("error with request sent");
//}
},
function(success2, callback) {
console.log('callback3');
//if(success2 === true) {
var success3;
Notification.findOneAndUpdate({userId: mongoose.Types.ObjectId(req.body.otherUser)}, {
$inc: {notifications: 1}
}, function(err, notify) {
if(err) {
res.send(err);
} else {
console.log(notify);
if(notify.added_notifications === true) {
// enable mail and include general u have got a new request... do not include name because not storing it
}
}
success3 = true;
callback(null, success3);
}],
function(err, results) {
res.json({response: true});
console.log("Add successful");
});
};
Notes: dbnotif is a model being called by mongoose,
userID is a global variable available to the file
I helped him solve this question offline, but we ended up using an EventEmitter as a proxy.
// main.js
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
Then add it to each request as middleware:
// elsewhere in main.js
app.use(function(req, res, next) {
req.emitter = emitter;
next();
});
Then in external routes file:
// routes.js
exports.addContactPost = function(req, res, err) {
req.emitter.emit( 'some-key', whatever, data, you, want );
};

Resources