Bluebird with mongoose using Promise.Each - javascript

I'm stuck in a function I'm working with ( I can be doing this all wrong ). So a quick explanation, I want to add bulk data in a collection, the collection is called "Sites" the format of the CSV is site,country,type. I'm trying to use promises for this (Bluebird). So consider the code:
Promise.each(sites, sites => new Promise((resolve, reject) => {
//console.log(sites);
let name = tools.extractDomain(req, res, sites[0]);
let country = sites[1];
let group = sites[2];
if (name != "" && country != "" && group != "") {
Site.findOne({ name: name }, "_id", function(err, duplicate) {
if (false) {
console.log("Duplicate site: " + duplicate);
} else {
//console.log("Adding " + name)
let site = new Site()
site.name = name
site.meta = {}
site.group = group
site.country = country
site.geomix = []
site.addedBy = req.user._id
site.addedAt = Date.now()
site.saveAsync().then(function(response){
tools.saveHistory(req, res, response._id, response.name, "Website Meta fetched.");
tools.saveHistory(req, res, response._id, response.name, "Link added for the first time."); //Save in history
resolve(site);
}).catch(function (e){
console.log(name);
reject();
});
}
});
}else{
console.log('Wrong Format');
}
}).then((data) => {
console.log('All websites processed!');
addedSites.push(data);
}).catch(err => {
//console.error('Failed');
}));
res.send({ status: 'ok', message: ''});
I'm making ajax calls so I return a res.send({ status: 'ok', message: ''}), I know that its in the incorrect place and I want to send some data along the res.send. Currently it sends the headers before the code actually finishes. I want to send the headers after all the data is added in Mongo but for every each in this case he resolve() so if I send the headers inside the ".then" of the ".each" I will get headers already sent error.
This might be a bit confusing. I feel I'm not doing this right. I'm going a bit crazy as well as I can't find a proper example that I can understand and implement.
But in the end my main question is: using an Ajax call what's the proper way to add let's say 1000 records in a collection using promises and actually control properly those who fail to add and those who don't?
Right now my code actually works but the logic is wrong for sure.
Thanks.

You can use bulkWrite on your model.
Ref: http://mongoosejs.com/docs/api.html#model_Model.bulkWrite
EDIT:
Sorry I misunderstood you. You need to move res.send({ status: 'ok', message: ''}); to then() and catch() blocks, so you will get something like this:
Promise.each(sites, sites => new Promise((resolve, reject) => {
// stuff you did before
}).then((data) => {
console.log('All websites processed!');
addedSites.push(data);
res.send({ status: 'ok', message: ''});
}).catch(err => {
res.send({ status: 'failed', message: err.message});
}));

This is what I came too, if someone can tell me if this is a good arch.
exports.addBulkSite = function(req, res, next) {
let siteArray = csv.parse((req.body.sites).trim()),
addedSites = [],
failedSites = [],
duplicated = [],
sites = siteArray,
size = sites.length,
processed = 0,
meta;
Promise.each(sites, sites => new Promise((resolve, reject) => {
let name = tools.extractDomain(req, res, sites[0]),
country = sites[1],
group = sites[2];
if (name != "" && country != "" && group != "") {
Site.findOneAsync({ name: name }, "_id").then(function(duplicate) {
duplicated.push(duplicate);
reject({name:name, message: 'Duplicated', critical:false});
}).catch(function(notDuplicated){
let site = new Site()
site = {
name: name,
meta: {},
group: group,
country: country, geomix:{},
addedBy: req.user._id,
addedAt:Date.now()
}
site.saveAsync().then(function(response){
tools.saveHistory(req, res, response._id, response.name, "Website Meta fetched.");
tools.saveHistory(req, res, response._id, response.name, "Link added for the first time."); //Save in history
resolve(site);
}).catch(function (e){
console.log(e);
reject({name:name, message: 'Error saving in the database. Please contact the administrator.', critical: true});
});
});
}else{
reject({name:name, message: 'Paramaters are missing', critical:false});
}
}).then((data) => {
processed++;
addedSites.push(data);
if(processed==size){
console.log('out');
res.send({ status: 'ok', addedSites: addedSites, failedSites: failedSites, duplicated: duplicated});
}
}).catch((err) => {
processed++;
console.log(err);
failedSites.push(err);
if(processed==size){
console.log('out');
res.send({ status: 'ok', addedSites: addedSites, failedSites: failedSites, duplicated: duplicated});
}
}));
}

Related

How can I fix double running function in firebase and react project?

I am trying to check existing of data on firebase real-time database and then add new data.
When I add exist data, it works well.
When I add new data, it works two time and don't add new data.
registerStaff = async (model) => {
if (!firebase.apps.length) {
return false;
}
if (model) {
return new Promise((resolve, reject) => {
this.db.ref("tbl_phone_number").on("value", async (snapshot) => {
if (snapshot.exists()) {
const samePhone = await _.filter(snapshot.val(), (o) => {
`find same phone number`;
return o.phone.toString() === model.phone.toString();
});
console.log("checking...", snapshot.val());
if (samePhone.length > 0) {
`checking samephone Number`;
console.log("exist...");
`if exist, return error`;
resolve({
type: "phone",
message: "The phone number is already used.",
});
} else {
`If there is no, add new phone number`;
const newPostKey = this.db
.ref()
.child("tbl_phone_number")
.push().key;
this.db
.ref(`tbl_phone_number/${newPostKey}`)
.set({ phone: model.phone, type: model.type })
.then(() => {
console.log("making...==>");
resolve({
type: "success",
message: "Successfully registered.",
});
})
.catch((err) => {
resolve({
type: "phone",
message: "Sorry. Something went wrong",
});
});
}
}
});
});
}
};
//console log result checking... checking... exist... checking... exist... making...
I found solution to solve this problem in using function "on" and "once".
'on' function of firebase is used to keep data from firebase and add new data automatically when I add new data.
'once' function of firebase is used to change data only once.
Therefore in above question, if I use 'once' function instead of 'on', it will work well.
Good luck.

JavaScript - Trying to filter data

Whenever I use this function, it will not retrieve data without specifying a title.
The point of this is supposed to be to filter through looking for authorid, and then search.
But if no search "title" is provided, I still want to return all of the Cards.
Can you tell me what in my code I would need to correct in order for this to happen?:
//Finds All of the User's Cards, and allows Searching by Title
exports.findMyCards2 = (req, res) => {
const { page, size, title, authorid } = req.query;
const { limit, offset } = getPagination(page, size);
Card.findAndCountAll({
limit,
offset,
where: {
authorid: { [Op.like]: `%${authorid}%` },
title: { [Op.like]: `%${title}%` || ""} // I tried to add || "" here
}
})
.then(data => {
const response = getPagingData(data, page, limit);
res.send(response);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving Cards."
});
});
};
An SQL LIKE "" is looking for empty values.
So you need to send a different WHERE clause to the DB if there is no title in the request.
//Finds All of the User's Cards, and allows Searching by Title
exports.findMyCards2 = (req, res) => {
const { page, size, title, authorid } = req.query;
const { limit, offset } = getPagination(page, size);
// Your default query
let query = {
authorid: { [Op.like]: `%${authorid}%` },
title: { [Op.like]: `%${title}%`}
}
// Query if the title is undefined (Remove the a title criteria of the WHERE)
if(!title){
query = {
authorid: { [Op.like]: `%${authorid}%` }
}
}
Card.findAndCountAll({
limit,
offset,
where: query // Then use it here
})
.then(data => {
const response = getPagingData(data, page, limit);
res.send(response);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving Cards."
});
});
};

Firebase: can't write Username + Title to DB [duplicate]

So I'm following a Savvy Apps tutorial in order to learn Vue.js. This tutorial uses Firebase with Firestore. Since Firestore is in Beta (as the tutorial says), changes might happen - and I think that might be the case here.
In any case, I'm trying to sign up a new user. I fill out the form and click 'Sign up' and I get this error message:
Error: Function CollectionReference.doc() requires its first argument to be of type string, but it was: undefined
But looking in Firebase, I see that the user has been created. So why do I get this error message? What is the first argument?
The code for signup looks like this:
signup() {
this.performingRequest = true;
fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password).then(user => {
this.$store.commit('setCurrentUser', user);
// create user obj
fb.usersCollection.doc(user.uid).set({
name: this.signupForm.name,
title: this.signupForm.title
}).then(() => {
this.$store.dispatch('fetchUserProfile');
this.performingRequest = false;
this.$router.push('/dashboard')
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
},
Let me know if you need more code - this is the first time I'm testing Vue.js.
createUserWithEmailAndPassword() returns a Promise containing a UserCredential. UserCredential has a property user for the firebase.User object.
You need to make the appropriate changes to your code to correctly access the UID:
signup() {
this.performingRequest = true;
fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password)
.then(credential=> { // CHANGED
this.$store.commit('setCurrentUser', credential.user); // CHANGED
// create user obj
fb.usersCollection.doc(credential.user.uid).set({ //CHANGED
name: this.signupForm.name,
title: this.signupForm.title
}).then(() => {
this.$store.dispatch('fetchUserProfile');
this.performingRequest = false;
this.$router.push('/dashboard')
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
},

Nodejs Mocha: Unable to test a POST and GET by ID

Trying to test a POST request and GET by ID. For the POST error it states: "expected 200, got 400". Then for the 3 GET by IDs, the first two is "Error: Timeout of 2000ms exceeded......", then gives me the two IDs a few minutes later. Then the third: "Expected 404, got 400".
Tried looking at docs for expect, supertest and mocha and couldnt find a solution. Those 3 is what i use for this testing
Here is the POST test
describe('POST /drinks', () => {
it('should create a new drink', (done) => {
let type = 'coffee';
let name = 'testName';
let image = 'testImage';
request(app)
.post('/drinks')
.send({
type,
name,
image
}).expect(200).expect((res) => {
expect(res.body.type, res.body.name, res.body.image).toBe(text);
}).expect((res) => {
expect(res.body.rating).toBe(number);
}).end((err, res) => {
if (err) {
return done(err);
}
Coffee.find({
type
}).then((feedData) => {
expect(feedData.length).toBe(1);
expect(feedData[0].type).toBe(text);
done();
}).catch(e => done(e));
});
});
});
Then heres the GET by ID:
describe('GET /drinks/:id', () => {
it('should return individual drink document', (done) => {
request(app)
.get(`/drinks/${feedData[0]._id.toHexString()}`)
.expect(200)
.expect(res => {
expect(res.body.drink.text).toBe(feedData[0].text);
})
.end((err, res) => {
if (err) return done(err);
done();
});
});
it('should return 404 if drink is not found', (done) => {
let hexId = new ObjectID().toHexString();
request(app)
.get(`/drinks/${hexId}`)
.expect(404)
.end((err, res) => {
if (err) return done(err);
done();
});
});
it('should return 404 for non-object ids', (done) => {
request(app)
.get('/drinks/123abc')
.expect(404)
.end((err, res) => {
if (err) return done(err);
done();
});
});
});
Heres my route for POST:
// POST a drink
exports.postDrinks = (req, res) => {
let type = req.body.type;
if (!type) {
res.status(400).send('Request parameters missing');
}
let newDrink;
// Default Drink Fields
let defaultFields = {
type,
name: req.body.name,
tastingNotes: req.body.tastingNotes,
comments: req.body.comments,
image: req.body.image,
rating: req.body.rating
}
// Determine which type and store it as that type
switch (type) {
case 'beer':
newDrink = new Beer({
...defaultFields,
style: req.body.style,
source: req.body.source,
});
break;
case 'coffee':
newDrink = new Coffee({
...defaultFields,
beanType: req.body.beanType,
brewTime: req.body.brewTime,
strength: req.body.strength
});
break;
case 'liquor':
newDrink = new Liquor({
...defaultFields,
typOfLiquor: req.body.typOfLiquor
});
break;
case 'tea':
newDrink = new Tea({
...defaultFields,
leafType: req.body.leafType,
steepTime: req.body.steepTime,
});
break;
default:
console.log('Please select an apprioriate drink');
break;
}
// Saves POST and sends it back as well. If not, then error
newDrink.save().then((drink) => {
res.send(drink);
}, (e) => {
res.status(400).send(e);
});
}
Heres my route for GET by ID:
/ GET by ID
exports.getIndividualDrink = (req, res) => {
let id = req.params.id;
// Show everything but id and v
Drink.findById(id).select('-_id -__v').then((drink) => {
// Check if theres that drink and ID is valid
if (!drink && !ObjectID.isValid(id)) {
return res.status(401).send();
}
// If there is, then send it back
res.send({
drink
});
}, (e) => {
res.status(400).send(e);
});
};
Expected should be passing, but like i said the results are:
1) POST: 'Error: expected 200, got 400'
2) First two GET by ID: 'Error: Timeout of 2000ms exceeded. ....'
3) Last GET by ID: 'Expected 404, got 400'
The 400 Bad Request error is an HTTP status code that means that the request you sent to the server, was somehow incorrect or corrupted and the server couldn't understand it.
Try to check your schema, you should post all required item if you miss something 400 is thrown.

meteor server insert data without login

I want to create API that allow other app to insert(create) new data. But so far I tried, this not work with error message "User id is required", I know that happen because no login user found when insert new data. Is it possible to insert new data without login or any possible way to login from server side if using accounts-password's package or any possible way to make this work?
code on server
Picker.route('/createFood/:title', function(params, req, res, next) {
console.log('-> params : ',params);
let username = (new Date()).getTime().toString();
function createFood() {
Fiber(function() {
console.log("-> username : ",username);
let acc = Accounts.createUser({
username: username,
email: username +'#foodie.com',
password: username
});
if (acc) {
console.log("-> acc : ",acc);
// Meteor.call("setUserId", acc);
Menus.insert({
title: params.title,
}, function(err, foodId) {
console.log("-> abs : ",Meteor.absoluteUrl());
console.log("-> err : ",err.message);
console.log("-> foodId : ",foodId);
let code, result;
if (err) {
code = 500;
result = {
error: err.message
}
} else {
code = 200;
result = {
foodId: foodId,
}
}
res.setHeader( 'Content-Type', 'application/json' );
res.statusCode = code;
res.end( JSON.stringify( result ) );
})
}
}).run();
}
if (params.title)
createFood();
});
code food model, there is userId owner here
if (Meteor.isServer) {
Menus.allow({
insert: function() {
return true;
},
update: function() {
return true;
},
remove: function() {
return true;
},
fetch: ['foodId'],
});
Menus.after.insert((userId, doc) => {
....
})
}
There is no reason why you can't insert to the database without logging in. You don't even have to include the accounts package if you don't want to .
Your current code doesn't insert unless a user is present, you can simplify it to this...
function createFood() {
Menus.insert({
title: params.title,

Categories