This is supposed to be a simple connection to access a localhost database.
Important information: I've tried very similar code, if not the exact same, and it works. The difference is I didn't put the connection in a class variable (this.connection) but because it's not async I didn't think much of it. So maybe it's simply a just javascript syntax related issue. Any tips would be greatly appreciated.
class Request {
constructor(connection) {
this.connection = mysql.createConnection(connection);
} // The parameter that gets passed is a dictionary { host: 'xxx', user: "xxx" ...
sendMessage(message) {
let arr = message.content.substring(1,message.length).toLowerCase().split(' '); // unimportant
arr = arr.filter(function (el) {return el != '';}); // unimportant
const promise = new Promise((resolve, reject) => {
this.connection.connect(function(err) {
console.log(this.connection); // This returns a description of the connection just fine
if (err) reject(err); // No error fires here
console.log(this.connection); // WHERE THINGS GO WRONG: Nothing gets printed on the console
this.connection.query('SELECT * FROM categories;', function (err, rows, fields) {
if (err) reject(err); // No error fires here
resolve(rows);
});
});
});
promise.then((result) => console.log(result)); // Nothing fires here either
There were two problems in your code:
When you reject with an error, you don't return, but continue the function
You were accessing this in anonymous non-lambda functions, which would overwrite this
This version has fixes for the above two issues applied:
sendMessage(message) {
let arr = message.content.substring(1, message.length).toLowerCase().split(' '); // unimportant
arr = arr.filter(function (el) { return el != ''; }); // unimportant
const promise = new Promise((resolve, reject) => {
this.connection.connect((err) => { // Using arrow function to keep original `this`
if (err) return reject(err); // If there's an error, return!
this.connection.query('SELECT * FROM categories;', function (err, rows, fields) {
if (err) return reject(err); // Again, if there's an error, return!
resolve(rows);
});
});
});
promise.then((result) => console.log(result)); // Nothing fires here either
}
Related
I want to write some tests for a method which reads from a JSON file (simulating a db) and returns the correct name, given that exists.
This is the code I have written for my method. It does throw an error when the id is not valid.
const getOne = (id, callback) => {
...
fs.readFile('db.json', (err, data) => {
if (err) {
throw new Error('Error reading file');
}
const person = JSON.parse(data)
.filter(el => el.id === id)
.map(el => el.name);
if (person.length === 0) {
throw new Error('It does not match DB entry');
}
callback(person);
});
...
The test I have written is:
it('Should reject an invalid id', (done) => {
api.getOne(100, (person) => {
try {
personFromDB = person;
} catch (error) {
assert.throws(() => {
}, new Error('It does not match DB entry'));
//done();
}
But it doesn't seem to pass the test. When I have the 'done()' uncommented, it passes the test, but I don't think it is because I pass the actual test, but rather because the test gets in the catch and executes the done() callback.
Any help, guidance or recommendation is much appreciated.
You won't be able to catch an Error being thrown in the fs.readFile callback.
Instead, pass any errors to the callback you pass to getOne.
Then you can check if an Error got passed to your callback in your test.
Here is a working example to get you started:
const fs = require('fs');
const assert = require('assert');
const api = {
getOne: (id, callback) => {
// ...
fs.readFile('db.json', (err, data) => {
if (err) return callback(err); // <= pass err to your callback
const person = JSON.parse(data)
.filter(el => el.id === id)
.map(el => el.name);
if (person.length === 0) return callback(new Error('It does not match DB entry')); // <= pass the Error to your callback
callback(null, person); // <= call the callback with person if everything worked
})
}
}
it('Should reject an invalid id', done => {
api.getOne(100, (err, person) => {
assert.strictEqual(err.message, 'It does not match DB entry'); // Success!
done();
});
});
I have 3 layer callbacks like this :
app.post('/', (req, res) => {
var filename = `outputs/${Date.now()}_output.json`;
let trainInput = req.files.trainInput;
let trainOutput = req.files.trainInput;
let testInput = req.files.trainInput;
//first
trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`, function (err) {
if (err) return res.status(500).send(err);
//second
trainOutput.mv(`inputs/${req.body.caseName}/train_output.csv`, function (err) {
if (err) return res.status(500).send(err);
//third
testInput.mv(`inputs/${req.body.caseName}/test_input.csv`, function (err) {
if (err) return res.status(500).send(err);
res.send('success');
});
});
});
});
In this case, there are only 3 file uploads. In another case, I have more than 10 file uploads, and it makes 10 layer callbacks. I know it because of JavaScript asynchronous.
Is there any way, with this case, to make a beautiful code? This is because when it 10 layer callbacks, the code looks horizontally weird.
Thanks
You can use the following code to make you code look better and avoid callback hell
app.post('/', async (req, res) => {
var filename = `outputs/${Date.now()}_output.json`;
let trainInput = req.files.trainInput;
let trainOutput = req.files.trainInput;
let testInput = req.files.trainInput;
try {
var result1 = await trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`);
var result2 = await trainInput.mv(`inputs/${req.body.caseName}/train_output.csv`);
var result2 = await testInput.mv(`inputs/${req.body.caseName}/test_input.csv`);
res.send('success');
}
catch (error) {
res.status(500).send(error);
}
});
You can make the functions return a Promise
I advice to make one function because you do the same thing 3 times. In this case I called the function 'save' but you can call it what ever you want. The first parameter is the file end the second the output filename.
function save(file, output) = return new Promise((resolve, reject) => {
file.mv(`inputs/${req.body.caseName}/${output}`, err =>
if (err) return reject(err)
resolve()
})
Promise.all([
save(req.files.trainInput, 'train_input.csv'),
save(req.files.trainInput, 'train_output.csv'),
save(req.files.trainInput, 'test_input.csv')
])
.then(_ => res.send(200))
.catch(err => res.send(400);
What version of Node you using? If async/await is available that cleans it up a bunch.
const moveCsv = (file, dest) => {
return new Promise((resolve, reject) => {
//third
file.mv(dest, function (err) {
if (err) reject(err);
resolve();
});
})
}
app.post('/', async(req, res) => {
try {
var filename = `outputs/${Date.now()}_output.json`;
const {
trainInput,
trainOutput,
testInput
} = req.files;
const prefix = `inputs/${req.body.caseName}`;
await moveCsv(trainInput, `${prefix}/train_input.csv`);
await moveCsv(trainOutput, `${prefix}/train_output.csv`);
await moveCsv(testInput, `${prefix}/test_input.csv`);
res.send('success');
} catch(err) {
res.status(500).send(err);
}
});
I'm also assuming here that your trainInput, trainOutput, testOutput weren't all meant to be req.files.trainInput.
Just be careful since the synchronous nature of the await calls are thread blocking. If that writer function takes ages you could also looking at putting those calls onto a worker thread. Won't really matter if your requests to that server endpoint are fast and non-frequent.
You can add RXJS to your project and use Observables.forkJoin()
Solution with Observables(assuming that trainInput.mv() returns Observable):
/* Without a selector */
var source = Rx.Observable.forkJoin(
trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`),
trainInput.mv(`inputs/${req.body.caseName}/train_output.csv`),
trainInput.mv(`inputs/${req.body.caseName}/test_input.csv`)
);
var subscription = source.subscribe(
function (x) {
// On success callback
console.log('Success: %s', x);
},
function (err) {
// Error callback
console.log('Error');
},
function () {
// Completed - runs always
console.log('Completed');
});
// => Success: [result_1, result_2, result_3] or Error
// => Completed
This may be a stupid question but I was not able to find an answer.
My db query (node -> PostgreSQL) is not firing the callback when the query function itself is called from another callback like this:
routes.js
router.post("/getsheet/", (req, res) => {
googleAPI.getSheet(googleToken, req.body.sheetid).then((invoices) => {
templateData.sheetData = invoices;
templateData.sheetData.length = invoices.length;
// When sheet is received, db query runs but it's callback won't
// function takes an array and index
db.query(invoices, 1).then((db_results) => {
console.log(db_results);
res.redirect("/");
}).catch((db_error) => {
console.error(db_error)
res.redirect("/");
});
}).catch((error) => {
console.error(error);
});
});
db.js
const query = (data, index) => {
return new Promise((resolve, reject) => {
// console log fires but not the callback function so it does not resolve or reject
console.log("... querying index ... " + index + " customer: " + data[index].name);
client.query(`SELECT * FROM customer WHERE customer_number=${data[index].customer_id};`, (err, res) => {
// does not run this block
if (!err) {
resolve(res);
} else {
return reject(err);
}
});
});
}
Thanks! :)
Have you already established a connection? You'd need to do that first before trying to run a query. See an example here:
http://mherman.org/blog/2015/02/12/postgresql-and-nodejs/#.WeJpJWhSyUk
pg.connect(connectionString, (err, client, done) => {
// Handle connection errors
if(err) {
done();
console.log(err);
return res.status(500).json({success: false, data: err});
}
// SQL Query > Select Data
const query = client.query('SELECT * FROM items ORDER BY id ASC;');
// Stream results back one row at a time
query.on('row', (row) => {
results.push(row);
});
// After all data is returned, close connection and return results
query.on('end', () => {
done();
return res.json(results);
});
});
Also, I'd highly recommend you use npm pg-promise. Will simplify your life.
Lastly, you're also missing the point of promises. I'd encourage you to use the newer async/await. However if you don't want to and/or can't, at least see why you're just getting back into a callback hell... promises are in place to help reduce all the call back nesting. Really you should reduce the nest level of your promises, like so:
router.post("/getsheet/", (req, res) => {
googleAPI.getSheet(googleToken, req.body.sheetid).then((invoices) => {
templateData.sheetData = invoices;
templateData.sheetData.length = invoices.length;
// When sheet is received, db query runs but it's callback won't
// function takes an array and index
return db.query(invoices, 1);
}).then((db_results) => {
console.log(db_results);
res.redirect("/");
}).catch((error) => {
console.error(error);
res.redirect("/");
});
});
It might be a bit confusing what I'm asking but I'll try to be as clear as I can.
Basically I'm doing unit test with mocha/chai for my Data Access Layer of my Node.JS server. I'm using bluebird to return a promise and an SQLite Databases.
That's my function insert I want to test :
insert(sqlRequest, sqlParams, sqlRequest2) {
return new Promise(function (resolve, reject) {
let insertStatement = this.getDatabase().prepare(sqlRequest);
let getStatement = this.getDatabase().prepare(sqlRequest2);
insertStatement.run(sqlParams, err => {
console.log('this.changes = ', this.changes);
if (this.changes === 1) {
getStatement.all({ $id: this.lastID }, (err, rows) => {
if (err) {
console.log('entered second err');
reject(err);
} else {
resolve(rows[0]);
}
});
} else {
console.log('entered first err ');
reject(err);
}
});
}.bind(this));
}
And that's my test with mocha :
it('insert : Error 2st SQL query', function (done) {
const daoCommon = new DaoCommon();
daoCommon.getDatabase = () => {
return {
prepare: (sqlRequest) => {
return {
all: (sql, callback) => {
let err = {};
let rows = null;
callback(err, rows);
},
run: (sqlParams, callback) => {
let err = undefined;
callback(err);
}
}
}
}
}
daoCommon.insert('', '', '')
.then(success => {
expect.fail();
})
.catch(error => {
expect(error).to.eql({});
})
.finally(function () {
done();
})
});
I want to simulate a test where the this.changes is equal to 1 but I don't know how/where I can set this value. According to what I've read this this object is from the callback function, but I have no idea exactly from where it comes or how to set it for my tests.
Update:
You can set the this of a function you are calling with .call of the method.
In your case calling callback with this.changes value will look like:
var thisObject = {
changes: 1
};
callback.call(thisObject, err);
This will set the value this.changes of your callback function.
The value of this is explained in the API documentation
If execution was successful, the this object will contain two
properties named lastID and changes which contain the value of the
last inserted row ID and the number of rows affected by this query
respectively.
It means that the callback function will always have this.changes. You can not change it unless you set this.changes = something manually, which I don't understand why would you do that.
Thank for #Maxali comment, I will post the answer below :
You can set this when calling the function callback(err) by using .call(). eg: callback.call({changes:1}, err). this will set changes to 1
And note that I had to change this line insertStatement.run(sqlParams, err => { where I have the callback from an arrow function to a function declaration insertStatement.run(sqlParams, function(err) { for this to work. I assume this is due to the this which in an arrow function doesn't refer to the object inside the function itself.
I've been trying to use gm with Bluebird like this:
var gm = require('gm');
var bluebird = require('bluebird');
gm = bluebird.promisifyAll(gm);
But then, when I try something like this:
gm(req.file.buffer)
.crop(tWidth, tHeight, tWidth/2, tHeight/2)
.gravity('Center')
.toBuffer()
.then(...)
I get this error:
gm().toBuffer() expects a callback.
If I only promisify the buffer method:
bluebird.promisify(gm(req.file.buffer)
.crop(tWidth, tHeight, tWidth/2, tHeight/2)
.gravity('Center')
.toBuffer)()
.then(buff => {...})
I get this error:
TypeError: this.stream is not a function
6:36:21 AM web.1 | at toBuffer (/Users/danielrvt/IdeaProjects/twe-backend/node_modules/gm/lib/command.js:162:17)
If I don't use promises, it works just fine.
That's not really how you're supposed to use the gm module as it is designed to provide a factory gm() and a number of cascadeable mutator functions like .resize().
Bluebird's .promisifyAll() only works on functions that accept a callback, which the majority of gm's functions do not.
If you wish to use gm with promises, you'll need to "promisify" it yourself, by wrapping your call as
function mutateAndSave() {
return new Promise( function(resolve,reject) {
try {
gm(image)
.doSomething()
.write(outputPath, function(err) {
if(err) {
throw(err);
}
resolve();
});
}
catch (err) {
reject(err);
}
});
}
After which, you can
mutateAndSave()
.then(...)
.catch(...);
UPDATE
Here are two ways to do what you want, but...
You'll note that both are a lot more complicated than just using gm as it is intended. ;)
Here's a way to do what you want with an event state machine.
const gm = requre('gm');
const EventEmitter = require('events');
const input_path = './image.jpg'
const output_path = './newimage.jpg';
const worker = new EventEmitter(); // create an event worker
// define the worker states - note: NO error checking! Muy mal!
const event_states={
start:() => worker.emit('resize',gm(input_path)), // creates a new gm image instance
resize:(img) => worker.emit('crop', img.resize(100,100)), // resizes the image
crop:(img) => worker.emit('write', img.crop(2,2,50,50)), // crops it
write:(img) => { // and writes it to the filesystem
img.write(output_path, err => {
if(err) {
worker.emit('error',err);
return;
}
worker.emit('complete');
});
},
error: (err) => console.error(err.toString()), // report error
complete: () => console.log('complete') // alert on completion
};
// add the states to the worker as event handlers
Object.keys(event_states).forEach(k => worker.on(k, event_states[k]));
// and fire it up...
worker.emit('start');
Or if you really, REALLY want to use Promises...
const writer = function (img) {
return new Promise( (resolve, reject) => {
img.write(output_path,err => {
if(err) {
reject(err);
return;
}
resolve(true);
});
});
};
const reader = function (input_path) {
return new Promise( (resolve,reject) => {
let img;
try {
img = gm(input_path);
}
catch (err) {
reject(err);
return;
}
resolve(img);
});
};
reader('./image.jpg')
.then( img => { return img.resize(100,100) }) // the return here is for illustration only
.then( img => img.crop(2,2,50,50)) // since this does exactly the same thing with less typing!
.then( writer )
.then( res => console.log('complete'))
.catch( err => console.error(err.toString()));
Again, lots more typing and complexity all to use the newest "shiny" thing. ;)