Node JS - Busboy retrieve parameters - javascript

I'm now stuck with my project using NodeJS and Busboy.
I'm trying to retrieve all the variables from my router and send it to controllers.
app.js (routes)
router.post('/product', function (req, res) {
var fields = {};
req.busboy.on('field', function(fieldname, val) {
fields[fieldname] = val;
});
req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
fields[filename] = file;
});
req.pipe(req.busboy);
productController.addNewProduct(fields, function (data) {
res.json(data);
});
});
ProductController.js
addNewProduct: function (params, callback) {
productLogic.addNewProduct(params, function (data) {
callback(data);
});
},
ProductLogic.js
addNewProduct: function (params, callback) {
Product.findOne({ name: params.name }, function (err, product) {
if(err) callback({ status: false, message: err });
if(product)
callback({ status: false, message: 'Produk sudah ada.' });
var newProduct = new Product({
name: params.name,
category_id: params.category_id,
description: params.description,
is_active: true
});
newProduct.save(function (err, result) {
if(err) callback({ status: false, message: err });
callback({ status: true, message: result });
});
});
},
My goal is to process the data all at once. And now I'm not sure if I can achieve it with busboy.
Please help me on this.
Bunch of thanks in advance

You should wait for busboy to finish parsing your form before calling your method.
busboy.on('finish', function() {
productController.addNewProduct(fields, function (data) {
res.json(data);
});
});

I think I already found the answer.
Thanks to marton for the heads up.
I've tried:
busboy.on('finish', function() {});
but it never emitted.
The reason is I have to file.resume() in my busboy.on('file'), example:
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
fields[fieldname] = file;
file.resume();
Once again thanks :)

Related

callback is not a function - castv2

I'm following this http://siglerdev.us/blog/2021/02/26/google-home-message-broadcast-system-node-js/31 which uses this library castv2-client to send messages to my google home. It works. I get the messages no problem, but the code throws
C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72
callback(null, response.status.volume);
^
TypeError: callback is not a function
at C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72:5 ver.js:72
at fn.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\request-response.js:27:7)
at fn.emit (events.js:203:15)
at Channel.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\controller.js:16:10) s\receiver.js:72:5
at Channel.emit (events.js:198:13) lib\controllers\request-response.js:27:7)
at Client.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2\lib\channel.js:23:10) ient\lib\controllers\controller.js:16:10)
at Client.emit (events.js:203:15)
at PacketStreamWrapper.onpacket (C:\Users\Phil\Documents\google home\node_module\channel.js:23:10)s\castv2\lib\client.js:81:10)
at PacketStreamWrapper.emit (events.js:198:13) s\castv2\lib\client.js:81:10)
at TLSSocket.<anonymous> (C:\Users\Phil\Documents\google home\node_modules\castv2\lib\packet-stream-wrapper.js:28:16)
What's wrong with the code that is throwing this AND/OR how can I fix it so it's either more graceful in catching error and doesn't throw since the message still delivers to google home or fix it to not throw this at all?
I appreciate any help!
I believe it's here in the castv2-client library that it's referencing, but I haven't been able to make it happy.
ReceiverController.prototype.launch = function(appId, callback) {
this.request({ type: 'LAUNCH', appId: appId }, function(err, response) {
if(err) return callback(err);
if(response.type === 'LAUNCH_ERROR') {
return callback(new Error('Launch failed. Reason: ' + response.reason));
}
callback(null, response.status.applications || []);
});
};
my code
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');
var App = {
playin: false,
DeviceIp: "",
Player: null,
GoogleHome: function (host, url) {
var client = new Client();
client.connect(host, function () {
client.launch(DefaultMediaReceiver, function (err, player) {
client.setVolume({ level: 1 });
var media = {
contentId: url,
contentType: 'audio/mp3',
streamType: 'BUFFERED'
};
App.Player = player;
App.Player.load(media, { autoplay: true }, function (err, status) {
App.Player.on('status', function (status) {
if (status.playerState === "IDLE" && App.playin === false) {
client.close();
}
});
});
});
});
client.on('error', function (err) {
console.log('Error: %s', err.message);
client.close();
});
},
run: function (ip, text) {
App.DeviceIp = ip;
const url = googleTTS.getAudioUrl(text, {
lang: 'en-US',
slow: false,
host: 'https://translate.google.com',
});
App.GoogleHome(App.DeviceIp, url, function (res) {
console.log(res);
})
},
broadcast: function(text){
const ips = '192.168.0.15'.split(","); //From config, 192.168.68.105,192.168.68.107,192.168.68.124
for (var s of ips){
App.run(s, text);
}
}
}
App.broadcast("Broadcasted to all of the devices"); //Only works if you did step 4.5
The error you reported:
C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72
callback(null, response.status.volume);
^
TypeError: callback is not a function
at C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72:5
seems to be related to the invocation of the method setVolume in your client:
client.setVolume({ level: 1 });
Please, consider review the source code of receiver.js in the castv2-client library:
ReceiverController.prototype.setVolume = function(options, callback) {
var data = {
type: 'SET_VOLUME',
volume: options // either `{ level: 0.5 }` or `{ muted: true }`
};
this.request(data, function(err, response) {
if(err) return callback(err);
callback(null, response.status.volume);
});
};
The library is claiming because you aren't providing a proper callback when invoking that function.
I have never used the library but probably providing something similar to the following callback could be of help:
client.setVolume({ level: 1 }, function(err, volume) {
if (err) {
// Handle error as appropriate
console.log('Error on setVolume:', err);
} else {
console.log('Volume:', volume)
}
});
Your final code would look like this:
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');
var App = {
playin: false,
DeviceIp: "",
Player: null,
GoogleHome: function (host, url) {
var client = new Client();
client.connect(host, function () {
client.launch(DefaultMediaReceiver, function (err, player) {
client.setVolume({ level: 1 }, function(err, volume) {
if (err) {
// Handle error as appropriate
console.log('Error on setVolume:', err);
} else {
console.log('Volume:', volume)
}
});
var media = {
contentId: url,
contentType: 'audio/mp3',
streamType: 'BUFFERED'
};
App.Player = player;
App.Player.load(media, { autoplay: true }, function (err, status) {
App.Player.on('status', function (status) {
if (status.playerState === "IDLE" && App.playin === false) {
client.close();
}
});
});
});
});
client.on('error', function (err) {
console.log('Error: %s', err.message);
client.close();
});
},
run: function (ip, text) {
App.DeviceIp = ip;
const url = googleTTS.getAudioUrl(text, {
lang: 'en-US',
slow: false,
host: 'https://translate.google.com',
});
App.GoogleHome(App.DeviceIp, url, function (res) {
console.log(res);
})
},
broadcast: function(text){
const ips = '192.168.0.15'.split(","); //From config, 192.168.68.105,192.168.68.107,192.168.68.124
for (var s of ips){
App.run(s, text);
}
}
}
App.broadcast("Broadcasted to all of the devices"); //Only works if you did step 4.5

Youtube search API results not displaying in the EJS file

I am implementing YouTube Search API in my Website, when I am calling the api from my route the results are returned but the page is rendered while the results are being computed, I guess because of the asynchronous behavior.
My route through which I am calling the API:
router.get('/video/results/:search_query', middleware.ensureAuthenticated, function (req, res) {
query = req.params.search_query;
console.log(query);
var dataFinal;
var resp = youtube.search.list({
part: 'snippet',
q: query,
type: 'video'
},function (err, data, response) {
if (err) {
console.error('Error: ' + err);
res.json({
status: "error"
});
}
if (data) {
// console.log(typeof data);
dataFinal = data;
// return res.json({
// status: "ok",
// data: data
// });
console.log(data);
//res.render('resultsVideo',{results:data})
}
});
res.render('resultsVideo',{data:dataFinal})
});
Please tell me how can I call the API and use the results in my EJS file to display.
you can use function callback to get the desired result.
router.get('/video/results/:search_query', middleware.ensureAuthenticated, function (req, res) {
query = req.params.search_query;
console.log(query);
var dataFinal;
function resp() = youtube.search.list({
part: 'snippet',
q: query,
type: 'video'
},function (err, data, response) {
if (err) {
console.error('Error: ' + err);
res.json({
status: "error"
});
}
if (data) {
// console.log(typeof data);
return data;
// return res.json({
// status: "ok",
// data: data
// });
console.log(data);
//res.render('resultsVideo',{results:data})
}
});
res.render('resultsVideo',{data:resp()})
});

Cordova/Javascript: method as callback function

Moin,
I feel a little stupid. I try to convert the following function into methods of an object:
function listDir(path){
window.resolveLocalFileSystemURL(path,
function (fileSystem) {
var reader = fileSystem.createReader();
reader.readEntries(
function (entries) {
console.log(entries);
},
function (err) {
console.log(err);
}
);
}, function (err) {
console.log(err);
}
);
}
Source: Cordova list all files from application directory (WWW)
But any way I try it does not work. I don't know how to use the methods as callback function, especially how to set the arguments of the function. With my code I expect the output:
debug: listdir
debug: filesystem
debug: entries
[Array or something]
But I just get:
debug: listdir
debug: filesystem
This ist my Code:
function Filelist() {}
Filelist.prototype = {
methodErr: function (err) {
console.log('debug: error');
console.log(err);
},
methodEntries: function (entries) {
console.log('debug: entries');
console.log(entries);
},
methodFilesystem: function (fileSystem) {
console.log('debug: filesystem');
var reader = fileSystem.createReader();
reader.readEntries(this.methodEntries, this.methodErr);
},
methodListDir: function (path) {
console.log('debug: listdir');
window.resolveLocalFileSystemURL(
path,
this.methodFilesystem,
this.methodErr
);
}
}
fl = new Filelist();
$('.klicken').click(function () {
fl.methodListDir(cordova.file.applicationDirectory);
});
Where is the bug?
Thanks in advance!
Jan
I've got the solution:
I have to bind this in the callback method:
...
reader.readEntries(this.methodEntries.bind(this), this.methodErr.bind(this));
...
window.resolveLocalFileSystemURL(
path,
this.methodFilesystem.bind(this),
this.methodErr.bind(this)
);
...
Now it works :)

How to outsource upload function as service notde.js

I'm trying to write a piece of my upload code as a service, because I need that function overall in my software. My project use sails.js - here the doc for a service.
In a controller I got this code, which uploads a file and after success it calls the function saveTheCampaign() and saves the file information in the DB.
req.file('logo').upload({
maxBytes: 10000000,
saveAs: function (uploadFile, cb) {
cb(null, Date.now() + uploadFile.filename);
},
dirname: sails.config.appPath + '/assets/images/campaign/'
}, function (err, uploadedFiles) {
if (err) {
return res.json(500, err);
}
else if (uploadedFiles.length === 0) {
// proceed without files
res.json({ error: "No image found for upload!"})
}
else {
// Success: handle uploaded file
var fileName = uploadedFiles[0].fd.split('\\');
params["logo"] = fileName[fileName.length - 1];
sails.controllers.campaign.saveTheCampaign(params, req, res);
}
});
saveTheCampaign: function (params, req, res) { //...}
Now I wanted to write this snippet as a service. My service is called UploadService and has a function called upload(), services can take two(2) arguments, option and a callback function. So I tried this to call the upload function of my service:
UploadService.upload(options, sails.controllers.campaign.saveTheCampaign(params, req, res));
The problem is, the params of the callback function (params, req, res) are not known at the time of the call, I get them AFTER the upload function is finished. How can I handle this?
One way to make this happen by using Q Promise Library. The snippet below is a working example for the same. You'll need to set value for sails.config.appPath.
Routes.js
'POST /upload' : 'CampaignController.upload'
UploadService.js
let q = require("q"); // https://github.com/kriskowal/q
module.exports = {
upload: function(options) {
let deferred = q.defer();
options['req'].file(options['fileFieldName']).upload({
maxBytes: 10,
saveAs: function(uploadedFile, cb) {
cb(null, Date.now() + uploadedFile.filename);
},
dirname: sails.config.appPath + '/assets/images/'
}, function(err, uploadedFiles) {
if (err) {
deferred.reject(err);
} else if (uploadedFiles.length === 0) {
// proceed without files
deferred.reject("No image found for upload!");
} else {
// Success: handle uploaded file
let params = [];
var fileName = uploadedFiles[0].fd.split('\\');
params["logo"] = fileName[fileName.length - 1];
deferred.resolve(params)
}
});
return deferred.promise;
}
}
CampaignController.js
module.exports = {
upload: function(req, res) {
let options = [];
options['fileFieldName'] = 'logo';
options['req'] = req;
UploadService.upload(options)
.then((params) => {
sails.controllers.campaign.saveTheCampaign(params);
res.send("Campaign Saved Successfully");
})
.catch((err) => res.send(err))
},
saveTheCampaign: function(params) {
console.log(`campaign ${params['logo']} saved`);
}
}

mocha api test - PUT route

I have the following test in mocha that yields "Uncaught AssertionError: undefined == 'Ernest'. I had a sneaking suspicion that the test was actually finding the song instance that is created in the top of the test, and I believe this proves it. That being said, I'm not sure how to fix it.
This is an api written for a MEAN stack app, with mongoose as the ODM
test.js
it('can save a song', function(done) {
Song.create({ title: 'saveASong' }, function(error, doc) {
assert.ifError(error);
var url = URL_ROOT + '/create/song/saveASong';
superagent.
put(url).
send({
title: 'saveASong',
createdBy: 'Ernest'
}).
end(function(error, res) {
assert.ifError(error);
assert.equal(res.status, status.OK);
Song.findOne({}, function(error, song) {
assert.ifError(error);
assert.equal(song.title, 'saveASong');
assert.equal(song.createdBy, 'Ernest');
done();
});
});
});
});
my route:
//PUT (save/update) song from the create view
api.put('/create/song/:title', wagner.invoke(function(Song) {
return function(req, res) {
Song.findOne({ title: req.params.title}, function(error, song) {
if(error) {
return res.
status(status.INTERNAL_SERVER_ERROR).
json({ error: error.toString() });
}
song.save(function(error, song) {
if(error) {
return res.
status(status.INTERNAL_SERVER_ERROR).
json({ error: error.toString() });
}
return res.json({ song: song });
});
});
};
}));
UPDATE: I put in a console.log(res.body) right after the "end", and it did not include the "createdBy: Ernest" k/v pair. So I tried to alter the object being sent to another k/v pair (that is from schema, of course) and still nothing persisted. I do not receive any errors if I comment out the "assert.equal...'Ernest'" line.
My latest version of PUT route:
api.put('/create/song/:title', wagner.invoke(function(Song) {
return function(req, res) {
Song.findOneAndUpdate({ title: req.params.title}, req.body ,{ new: true }, function(error, song) {
if(error) {
return res.
status(status.INTERNAL_SERVER_ERROR).
json({ error: error.toString() });
}
return res.json({ song: song });
});
};
}));
the following api route
api.put('/create/song/:title', wagner.invoke(function(Song) {
return function(req, res) {
Song.findOneAndUpdate({ title: req.params.title}, req.body ,{ new: true }, function(error, song) {
if(error) {
return res.
status(status.INTERNAL_SERVER_ERROR).
json({ error: error.toString() });
}
return res.json({ song: song });
});
};
}));
passes the following mocha test
it('can save a song', function(done) {
Song.create({ title: 'saveASong' }, function(error, doc) {
assert.ifError(error);
var url = URL_ROOT + '/create/song/saveASong';
superagent.
put(url).
send({
sections:[{ name: 'intro', timeSig: 140 }]
}).
end(function(error, res) {
assert.ifError(error);
assert.equal(res.status, status.OK);
console.log(res.body);
Song.findOne({}, function(error, song) {
assert.ifError(error);
assert.equal(song.title, 'saveASong');
//console.log(song);
assert.equal(song.sections[0].name, 'intro');
done();
});
});
});
});

Categories