How are values being returned from those functions insiste async.parallel If they don't have a "Return" operator? - express

I am practicing on the LocalLibrary app exercise of Mozilla's Node/Express tutorial.
In the Express Async Module part, I can't understand how the results of each function inside the async.parallel are being retrieved, since those functions do not have a "Return" operator.
var Book = require('../models/book');
var Author = require('../models/author');
var Genre = require('../models/genre');
var BookInstance = require('../models/bookinstance');
var async = require('async');
exports.index = function(req, res) {
async.parallel({
book_count: function(callback) {
Book.countDocuments({}, callback); // Pass an empty object as match condition to find all documents of this collection
},
book_instance_count: function(callback) {
BookInstance.countDocuments({}, callback);
},
book_instance_available_count: function(callback) {
BookInstance.countDocuments({status:'Available'}, callback);
},
author_count: function(callback) {
Author.countDocuments({}, callback);
},
genre_count: function(callback) {
Genre.countDocuments({}, callback);
}
}, function(err, results) {
res.render('index', { title: 'Local Library Home', error: err, data: results });
});
};

Related

infinity loading when try to scrap title article with cheerio

infinity loading when try to scrap title article
i was trying to scrap title articles with cheerio in node js but i could not make it happen
infinity loading in google chrome and no content . here is my controller.js file that i trying to use cheerio to scrap title articles with cheerio and show handlesbars view engine
but view engie is working and everything is fine . but for scrap infinity loading in chrome with no error
classname that i use for this website is .c-entry-box--compact__title
maybe i am wrong . but i can not figure out
var express = require("express");
var router = express.Router();
var path = require("path");
var request = require("request");
var cheerio = require("cheerio");
var Comment = require("../models/Comment.js");
var Article = require("../models/Article.js");
router.get("/", function(req, res) {
res.redirect("/articles");
});
router.get("/scrape", function(req, res) {
request("http://www.theverge.com", function(error, response, html) {
var $ = cheerio.load(html);
var titlesArray = [];
$(".c-entry-box--compact__title").each(function(i, element) {
var result = {};
result.title = $(this)
.children("a")
.text();
result.link = $(this)
.children("a")
.attr("href");
if (result.title !== "" && result.link !== "") {
if (titlesArray.indexOf(result.title) == -1) {
titlesArray.push(result.title);
Article.count({ title: result.title }, function(err, test) {
if (test === 0) {
var entry = new Article(result);
entry.save(function(err, doc) {
if (err) {
console.log(err);
} else {
console.log(doc);
}
});
}
});
} else {
console.log("Article already exists.");
}
} else {
console.log("Not saved to DB, missing data");
}
});
res.redirect("/");
});
});
router.get("/articles", function(req, res) {
Article.find()
.sort({ _id: -1 })
.exec(function(err, doc) {
if (err) {
console.log(err);
} else {
var artcl = { article: doc };
res.render("index", artcl);
}
});
});
router.get("/articles-json", function(req, res) {
Article.find({}, function(err, doc) {
if (err) {
console.log(err);
} else {
res.json(doc);
}
});
});
router.get("/clearAll", function(req, res) {
Article.remove({}, function(err, doc) {
if (err) {
console.log(err);
} else {
console.log("removed all articles");
}
});
res.redirect("/articles-json");
});
router.get("/readArticle/:id", function(req, res) {
var articleId = req.params.id;
var hbsObj = {
article: [],
body: []
};
Article.findOne({ _id: articleId })
.populate("comment")
.exec(function(err, doc) {
if (err) {
console.log("Error: " + err);
} else {
hbsObj.article = doc;
var link = doc.link;
request(link, function(error, response, html) {
var $ = cheerio.load(html);
$(".l-col__main").each(function(i, element) {
hbsObj.body = $(this)
.children(".c-entry-content")
.children("p")
.text();
res.render("article", hbsObj);
return false;
});
});
}
});
});
router.post("/comment/:id", function(req, res) {
var user = req.body.name;
var content = req.body.comment;
var articleId = req.params.id;
var commentObj = {
name: user,
body: content
};
var newComment = new Comment(commentObj);
newComment.save(function(err, doc) {
if (err) {
console.log(err);
} else {
console.log(doc._id);
console.log(articleId);
Article.findOneAndUpdate(
{ _id: req.params.id },
{ $push: { comment: doc._id } },
{ new: true }
).exec(function(err, doc) {
if (err) {
console.log(err);
} else {
res.redirect("/readArticle/" + articleId);
}
});
}
});
});
module.exports = router;
The http://www.theverge.com Add content dynamically by scrolling.
This is example how to get the title by puppeteer
const puppeteer = require("puppeteer");
const getTitle = async () => {
try {
const browser = await puppeteer.launch({
headless: false,
});
const page = await browser.newPage();
await page.setDefaultNavigationTimeout(0);
await page.goto('https://www.theverge.com');
await page.setViewport({
width:1920,
height:1080
});
// scroll down end of page
await page.evaluate(() => {
window.scrollTo(0, window.document.body.scrollHeight);
});
await page.waitForNavigation({ waitUntil: 'networkidle0' }), // (0 network connections for 500ms)
// get the title
titles = await page.evaluate(() => {
const textSelector = 'div.inline.pr-4.font-bold'
texts = Array.from(document.querySelectorAll(textSelector), row => row.innerText.trim() );
return texts;
});
await browser.close();
return Promise.resolve(titles);
} catch (error) {
return Promise.reject(error);
}
}
getTitle()
.then((titles) => {
console.log(titles); // first news search
})
This is result
[
'Is an upgraded M2 Ultra enough for a new Mac Pro and the Mac Studio?',
'Here’s the official trailer for Christopher Nolan’s next IMAX-filmed epic, Oppenheimer.',
'Sam Bankman-Fried’s ready to surrender himself to the US for extradition.',
'Who knew the thumb drive had such a contentious origin story?',
'But how many pebbles do you have in a jar?',
'This way for The Way of Water.',
'Netflix is taking Blockbuster behind the woodshed again.',
'I can’t escape the year-end wrap-ups.',
'The clock’s ticking if you want to get your gifts on time.',
'Want solar panels on your California home? Now might be the time.',
'Twitter Spaces has returned.',
'Apple’s facing another accusation of breaking labor laws.',
'Every game should have this feature.',
'Google’s working on simplifying smart home control on the wrist.',
'Apple could open up iOS, and the feds finally make a case against SBF.',
'I’m not the first, and I won’t be the last... but I do feel early.',
'For what it’s worth, Avatar: The Way of Water’s a good looking movie.',
'You may not want to upgrade to Apple’s new Home architecture.'
]

How to access local component variable from a callback in vue?

I am trying to set my components variable using an api rest command. I wanted to handle all responses through a function in its own file called handleResponse() which is below.
// api/tools/index.js
function handleResponse (promise, cb, cbError) {
var cbErrorRun = (cbError && typeof cb === "function")
promise.then(function (response) {
if (!response.error) {
cb(response)
}
else if (cbErrorRun) {
cbError(response)
}
}).catch(function (error) {
console.log(error)
if (cbErrorRun) {
var responseError = {
"status": 404,
"error": true,
"message": error.toString()
}
cbError(responseError)
}
})
}
export {handleResponse}
In my component file I have this
.... More above....
<script>
import { fetchStock } from '#/api/stock'
export default {
data () {
return {
stock: {},
tabs: [
{
title: 'Info',
id: 'info'
},
{
title: 'Listings',
id: 'listings'
},
{
title: 'Company',
id: 'company'
}
],
}
},
validate ({params}) {
return /^\d+$/.test(params.id)
},
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
fetchStock(
params,
function(response) { //on successful data retrieval
this.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
function(responseError) { //on error
console.log(responseError)
}
)
}
}
</script>
The current code gives me this error: "Uncaught (in promise) TypeError: Cannot set property 'stock' of undefinedAc". I think this happens because I no longer have access to 'this' within the callback I pass in the fetchStock function. How would I fix this without changing the current handleResponse layout.
You can try this trick
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
var self = this;
fetchStock(
params,
function(response) { //on successful data retrieval
self.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
function(responseError) { //on error
console.log(responseError)
}
)
}
You can either use an arrow function for you callback since arrow functions maintain and use the this of their containing scope:
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
fetchStock(
params,
(response) => { //on successful data retrieval
self.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
(responseError) => { //on error
console.log(responseError)
}
)
}
Or you can assign const vm = this n the beginning of your method before the callbacks like so.
vm stands for "View Model"
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
const vm = this;
fetchStock(
params,
function(response) { //on successful data retrieval
self.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
function(responseError) { //on error
console.log(responseError)
}
)
}
I advise using the const as opposed to var in the vm declaration to make it obvious the value of vm is a constant.

Vee-validate (VueJS) - evaluating a condition asynchronously

Can I make a custom validation rule that returns true/false based on a AJAX request? the problem is that the validate call has finished running when the AJAX call completes.
Do I need to have the rule set/unset a boolean variable based on which the field is valid/invalid?
const isValidNameRule = {
getMessage(field)
{
return "The name must be unique."
},
validate(validatingName)
{
var formData = new FormData();
formData.append("validatingName", validatingName);
this.$http.post("/api/isValid?name=" + validatingName, formData)
.then(function (response) {
// success
return true;
}, function (response) {
// error
return false;
});
}
};
Didn't know how to work with Promises.
Eventually got it working by extending one of the official samples:
const customRule = {
getMessage(field, params, data) {
return (data && data.message) || 'Something went wrong';
},
validate(aValue) {
return new Promise(resolve => {
var formData = new FormData();
formData.append("nameFilter", aValue);
$.ajax({
type: "POST",
url: url,
data: {
action: "validate",
value: aValue,
}
}).done(function (data) {
if (!ok)
{
resolve({
valid: false,
data: {message: "Condition not met"}
});
}
else
{
resolve({
valid: !! aValue,
data: undefined
});
}
});
});
}
};

Test multiple http requests to express application using Jasmine

I have installed Jasmine CLI globally using npm install -g jasmine
I'm trying to test multiple http requests at once using test suite below, multiple calls per each requests were sent (seeing output of console.log() but nothing returned so the test was failure, please guide me is this possible to do so ? and how to do this ?
index.js
var app = require('express')();
var request = require('request');
app.get('/', function(req, res) {
console.log('GET /');
res.status(200);
res.send('Hello World');
});
app.listen(3000);
spec/multipleRequestSpec.js
var request = require('request');
var async = require('async');
describe('express application', function() {
var baseUrl = 'http://localhost:3000';
var statusCode = [0, 0];
var b = ['', ''];
beforeEach(function(done) {
async.parallel([
function() {
request.get(baseUrl, function(err, res, body) {
statusCode[0] = res.statusCode;
b[0] = body;
})
}
,
function() {
request.post(baseUrl, function(err, res, body) {
statusCode[1] = res.statusCode;
b[1] = body;
})
}
], done());
});
it('should return 200', function() {
expect(statusCode[0]).toBe(200);
});
it('should return hello world', function() {
expect(b[0]).toEqual('Hello World');
});
it('should return error 404', function() {
expect(statusCode[1]).toBe(404);
});
});
Edited
When testing only one request I place done() inside the request() it works just fine, but I quite confuse where to place done() when using async.pararell()
spec/requestSpec.js
var request = require('request');
describe('expresss application', function() {
var baseUrl = 'http://localhost:3000';
var statusCode = 0;
beforeEach(function(done) {
request.get(baseUrl, function(err, res, body) {
statusCode = res.statusCode;
done();
});
});
it('should return 200', function() {
expect(statusCode).toBe(200);
});
});
In describe block you initiate variable body. And you use it in it blocks. But in request.get and in request.post you have callback function with parameter body which is in use instead of your describe body variable.
Change beforeEach to:
beforeEach(function(done) {
async.parallel([
function(callback) {
request.get(baseUrl, function(err, res, reqBody) {
statusCode[0] = res.statusCode;
body[0] = reqBody;
callback();
})
}
,
function(callback) {
request.post(baseUrl, function(err, res, reqBody) {
statusCode[1] = res.statusCode;
body[1] = reqBody;
callback();
})
}
], done);
});
I think that you should also check err param in request callbacks. Because there may be errors which fails/pass your tests.
For api endpoints tests it is more easy to use superagent or supertest instead of request.

How to get socket.io to recognize when model has been updated

I'm relatively new to MEAN/Mongoose/socket.io and am missing something that seems like it'd be basic, but regardless, it's keeping me from moving forward.
I'm keeping track of a group's meetings and have the following Schema:
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ScheduleSchema = new Schema({
description: String,
meetingPlace: String,
office: String,
start: { type: Date, default: Date.now },
end: { type: Date, default: Date.now },
participants: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
author: {
type: Schema.Types.ObjectId,
ref: 'User'
},
timestamp: { type: Date, default: Date.now },
active: Boolean
});
ScheduleSchema.statics = {
load: function(cb) {
this.find({})
.populate({path:'participants'})
.exec(cb);
}
};
module.exports = mongoose.model('Schedule', ScheduleSchema);
When I create a new item in the Schedule model, this code sets it in motion (from a client-side controller):
$http.post('/api/schedules', { description: info.description, participants: attendees, meetingPlace: info.meetingPlace, office: info.office, start: info.start, end: info.end, timestamp: new Date(), active: true });
And because of this code:
'use strict';
var express = require('express');
var controller = require('./schedule.controller');
var router = express.Router();
router.get('/', controller.index);
router.get('/:id', controller.show);
router.post('/', controller.create);
router.put('/:id', controller.update);
router.patch('/:id', controller.update);
router.delete('/:id', controller.destroy);
module.exports = router;
I think the request is being routed to controller.create, which is the following:
'use strict';
var _ = require('lodash');
var Schedule = require('./schedule.model');
// Get list of schedules
exports.index = function(req, res) {
Schedule.load(function (err, schedules) { /*.find*/
if(err) { return handleError(res, err); }
return res.json(200, schedules);
});
};
// Get a single schedule
exports.show = function(req, res) {
Schedule.findById(req.params.id, function (err, schedule) {
if(err) { return handleError(res, err); }
if(!schedule) { return res.send(404); }
return res.json(schedule);
});
};
// Creates a new schedule in the DB.
exports.create = function(req, res) {
var promise = Schedule.create(req.body, function(err, schedule) {
if(err) { return handleError(res, err); }
return res.json(201, schedule);
});
};
// Updates an existing schedule in the DB.
exports.update = function(req, res){
var updatedMeeting = req.body;
var id = updatedMeeting._id;
delete updatedMeeting._id;
Schedule.update({_id : id}, updatedMeeting, { }, function (err, numberAffected, raw) {
if (err) return res.json(500, err);
updatedMeeting._id = id;
return res.json(201, updatedMeeting);
});
};
// Deletes a schedule from the DB.
exports.destroy = function(req, res) {
Schedule.findById(req.params.id, function (err, schedule) {
if(err) { return handleError(res, err); }
if(!schedule) { return res.send(404); }
schedule.remove(function(err) {
if(err) { return handleError(res, err); }
return res.send(204);
});
});
};
function handleError(res, err) {
return res.send(500, err);
}
After exports.create is run, and I don't know how, but something sends it over to here:
'use strict';
var Schedule = require('./schedule.model');
exports.register = function(socket) {
Schedule.schema.post('save', function (doc) {
onSave(socket, doc);
});
Schedule.schema.post('remove', function (doc) {
onRemove(socket, doc);
});
Schedule.schema.post('update', function (doc) {
onUpdate(socket, doc);
});
}
function onSave(socket, doc, cb) {
console.log('**********onSave**********');
Schedule
.findOne({ _id : doc._id })
.populate('participants')
.exec(function (err, event) {
if (err) return handleError(err);
socket.emit('schedule:save', event);
});
}
function onRemove(socket, doc, cb) {
socket.emit('schedule:remove', doc);
}
function onUpdate(socket, doc, cb) {
console.log('**********onUpdate**********');
socket.emit('schedule:update', doc);
}
and the line socket.emit('schedule:save', event); is called.
So that's all great, but when I update the Schedule model, I can't get socket to emit 'schedule:update' because it never gets to the "onUpdate" function in the code snippet just above this.
Starting from the client-side call:
$http.patch('/api/schedules/' + info._id, { _id: info._id, description: info.description, participants: attendees, meetingPlace: info.meetingPlace, office: info.office, start: info.start, end: info.end, timestamp: new Date(), active: true });
and the router sends that to exports.update, the model is updated, but socket doesn't emit anything, so all the clients connected see the old schedule information.
How and where can I relay to socket.io that the model has been updated? I think what I'm missing is how Schedule.schema.post('some action... gets called within the exports.register function, but I've been looking at this for a while and could use a hand - thanks.